/**
 * @namespace Oryx name space for different utility methods
 * @name ORYX.Utils
 */

if (!ORYX) var ORYX = {};

ORYX.Utils = {
  /**
   * General helper method for parsing a param out of current location url
   * @example
   * // Current url in Browser => "http://oryx.org?param=value"
   * ORYX.Utils.getParamFromUrl("param") // => "value"
   * @param {Object} name
   */
  getParamFromUrl: function (name) {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    var regexS = '[\\?&]' + name + '=([^&#]*)';
    var regex = new RegExp(regexS);
    var results = regex.exec(window.location.href);
    if (results == null) {
      return null;
    } else {
      return results[1];
    }
  },

  adjustLightness: function () {
    return arguments[0];
  },

  adjustGradient: function (gradient, reference) {
    if (ORYX.CONFIG.DISABLE_GRADIENT && gradient) {
      var col = reference.getAttributeNS(null, 'stop-color') || '#ffffff';

      $A(gradient.getElementsByTagName('stop')).each(function (stop) {
        if (stop == reference) {
          return;
        }
        stop.setAttributeNS(null, 'stop-color', col);
      });
    }
  },
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

XMLNS = {
  ATOM: 'http://www.w3.org/2005/Atom',
  XHTML: 'http://www.w3.org/1999/xhtml',
  ERDF: 'http://purl.org/NET/erdf/profile',
  RDFS: 'http://www.w3.org/2000/01/rdf-schema#',
  RDF: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  RAZIEL: 'http://b3mn.org/Raziel',

  SCHEMA: '',
};

//TODO kann kickstart sich vielleicht auch um die erzeugung von paketen/
// namespaces k???mmern? z.b. requireNamespace("ORYX.Core.SVG");
var Kickstart = {
  started: false,
  callbacks: [],
  alreadyLoaded: [],
  PATH: '',

  load: function () {
    Kickstart.kick();
  },

  kick: function () {
    //console.profile("loading");
    if (!Kickstart.started) {
      Kickstart.started = true;
      Kickstart.callbacks.each(function (callback) {
        // call the registered callback asynchronously.
        window.setTimeout(callback, 1);
      });
    }
  },

  register: function (callback) {
    //TODO Add some mutual exclusion between kick and register calls.
    with (Kickstart) {
      if (started) window.setTimeout(callback, 1);
      else Kickstart.callbacks.push(callback);
    }
  },

  /**
   * Loads a js, assuring that it has only been downloaded once.
   * @param {String} url the script to load.
   */
  require: function (url) {
    // if not already loaded, include it.
    if (Kickstart.alreadyLoaded.member(url)) return false;
    return Kickstart.include(url);
  },

  /**
   * Loads a js, regardless of whether it has only been already downloaded.
   * @param {String} url the script to load.
   */
  include: function (url) {
    // prepare a script tag and place it in html head.
    var head = document.getElementsByTagNameNS(XMLNS.XHTML, 'head')[0];
    var s = document.createElementNS(XMLNS.XHTML, 'script');
    s.setAttributeNS(XMLNS.XHTML, 'type', 'text/javascript');
    s.src = Kickstart.PATH + url;

    //TODO macht es sinn, dass neue skript als letztes kind in den head
    // einzubinden (stichwort reihenfolge der skript tags)?
    head.appendChild(s);

    // remember this url.
    Kickstart.alreadyLoaded.push(url);

    return true;
  },
};

// register kickstart as the new onload event listener on current window.
// previous listener(s) are triggered to launch with kickstart.
Event.observe(window, 'load', Kickstart.load);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

var ERDF = {
  LITERAL: 0x01,
  RESOURCE: 0x02,
  DELIMITERS: ['.', '-'],
  HASH: '#',
  HYPHEN: '-',

  schemas: [],
  callback: undefined,
  log: undefined,

  init: function (callback) {
    // init logging.
    //ERDF.log = Log4js.getLogger("oryx");
    //ERDF.log.setLevel(Log4js.Level.ALL);
    //ERDF.log.addAppender(new ConsoleAppender(ERDF.log, false));

    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace("ERDF Parser is initialized.");

    // register callbacks and default schemas.
    ERDF.callback = callback;
    ERDF.registerSchema('schema', XMLNS.SCHEMA);
    ERDF.registerSchema('rdfs', XMLNS.RDFS);
  },

  run: function () {
    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace("ERDF Parser is running.");

    // do the work.
    return ERDF._checkProfile() && ERDF.parse();
  },

  parse: function () {
    //(ERDF.log.isDebugEnabled())
    //	ERDF.log.debug("Begin parsing document metadata.");

    // time measuring
    ERDF.__startTime = new Date();

    var bodies = document.getElementsByTagNameNS(XMLNS.XHTML, 'body');
    var subject = { type: ERDF.RESOURCE, value: '' };

    var result = ERDF._parseDocumentMetadata() && ERDF._parseFromTag(bodies[0], subject);

    // time measuring
    ERDF.__stopTime = new Date();

    var duration = (ERDF.__stopTime - ERDF.__startTime) / 1000;
    //alert('ERDF parsing took ' + duration + ' s.');

    return result;
  },

  _parseDocumentMetadata: function () {
    // get links from head element.
    var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head');
    var links = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'link');
    var metas = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'meta');

    // process links first, since they could contain schema definitions.
    $A(links).each(function (link) {
      var properties = link.getAttribute('rel');
      var reversedProperties = link.getAttribute('rev');
      var value = link.getAttribute('href');

      ERDF._parseTriplesFrom(ERDF.RESOURCE, '', properties, ERDF.RESOURCE, value);

      ERDF._parseTriplesFrom(ERDF.RESOURCE, value, reversedProperties, ERDF.RESOURCE, '');
    });

    // continue with metas.
    $A(metas).each(function (meta) {
      var property = meta.getAttribute('name');
      var value = meta.getAttribute('content');

      ERDF._parseTriplesFrom(ERDF.RESOURCE, '', property, ERDF.LITERAL, value);
    });

    return true;
  },

  _parseFromTag: function (node, subject, depth) {
    // avoid parsing non-xhtml content.
    if (!node || !node.namespaceURI || node.namespaceURI != XMLNS.XHTML) {
      return;
    }

    // housekeeping.
    if (!depth) depth = 0;
    var id = node.getAttribute('id');

    // some logging.
    //if(ERDF.log.isTraceEnabled())
    //	ERDF.log.trace(">".times(depth) + " Parsing " + node.nodeName + " ("+node.nodeType+") for data on " +
    //		((subject.type == ERDF.RESOURCE) ? ('&lt;' + subject.value + '&gt;') : '') +
    //		((subject.type == ERDF.LITERAL) ? '"' + subject.value + '"' : ''));

    /* triple finding! */

    // in a-tags...
    if (node.nodeName.endsWith(':a') || node.nodeName == 'a') {
      var properties = node.getAttribute('rel');
      var reversedProperties = node.getAttribute('rev');
      var value = node.getAttribute('href');
      var title = node.getAttribute('title');
      var content = node.textContent;

      // rel triples
      ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.RESOURCE, value, function (triple) {
        var label = title ? title : content;

        // label triples
        ERDF._parseTriplesFrom(triple.object.type, triple.object.value, 'rdfs.label', ERDF.LITERAL, label);
      });

      // rev triples
      ERDF._parseTriplesFrom(subject.type, subject.value, reversedProperties, ERDF.RESOURCE, '');

      // type triples
      ERDF._parseTypeTriplesFrom(subject.type, subject.value, properties);

      // in img-tags...
    } else if (node.nodeName.endsWith(':img') || node.nodeName == 'img') {
      var properties = node.getAttribute('class');
      var value = node.getAttribute('src');
      var alt = node.getAttribute('alt');

      ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.RESOURCE, value, function (triple) {
        var label = alt;

        // label triples
        ERDF._parseTriplesFrom(triple.object.type, triple.object.value, 'rdfs.label', ERDF.LITERAL, label);
      });
    }

    // in every tag
    var properties = node.getAttribute('class');
    var title = node.getAttribute('title');
    var content = node.textContent;
    var label = title ? title : content;

    // regular triples
    ERDF._parseTriplesFrom(subject.type, subject.value, properties, ERDF.LITERAL, label);

    if (id) subject = { type: ERDF.RESOURCE, value: ERDF.HASH + id };

    // type triples
    ERDF._parseTypeTriplesFrom(subject.type, subject.value, properties);

    // parse all children that are element nodes.
    var children = node.childNodes;
    if (children)
      $A(children).each(function (_node) {
        if (_node.nodeType == _node.ELEMENT_NODE) ERDF._parseFromTag(_node, subject, depth + 1);
      });
  },

  _parseTriplesFrom: function (subjectType, subject, properties, objectType, object, callback) {
    if (!properties) return;
    properties
      .toLowerCase()
      .split(' ')
      .each(function (property) {
        //if(ERDF.log.isTraceEnabled())
        //	ERDF.log.trace("Going for property " + property);

        var schema = ERDF.schemas.find(function (schema) {
          return (
            false ||
            ERDF.DELIMITERS.find(function (delimiter) {
              return property.startsWith(schema.prefix + delimiter);
            })
          );
        });

        if (schema && object) {
          property = property.substring(schema.prefix.length + 1, property.length);
          var triple = ERDF.registerTriple(
            new ERDF.Resource(subject),
            { prefix: schema.prefix, name: property },
            objectType == ERDF.RESOURCE ? new ERDF.Resource(object) : new ERDF.Literal(object)
          );

          if (callback) callback(triple);
        }
      });
  },

  _parseTypeTriplesFrom: function (subjectType, subject, properties, callback) {
    if (!properties) return;
    properties
      .toLowerCase()
      .split(' ')
      .each(function (property) {
        //if(ERDF.log.isTraceEnabled())
        //	ERDF.log.trace("Going for property " + property);

        var schema = ERDF.schemas.find(function (schema) {
          return (
            false ||
            ERDF.DELIMITERS.find(function (delimiter) {
              return property.startsWith(ERDF.HYPHEN + schema.prefix + delimiter);
            })
          );
        });

        if (schema && subject) {
          property = property.substring(schema.prefix.length + 2, property.length);
          var triple = ERDF.registerTriple(
            subjectType == ERDF.RESOURCE ? new ERDF.Resource(subject) : new ERDF.Literal(subject),
            { prefix: 'rdf', name: 'type' },
            new ERDF.Resource(schema.namespace + property)
          );
          if (callback) callback(triple);
        }
      });
  },

  /**
   * Checks for ERDF profile declaration in head of document.
   */
  _checkProfile: function () {
    // get profiles from head element.
    var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head');
    var profiles = heads[0].getAttribute('profile');
    var found = false;

    // if erdf profile is contained.
    if (profiles && profiles.split(' ').member(XMLNS.ERDF)) {
      // pass check.
      //if(ERDF.log.isTraceEnabled())
      //	ERDF.log.trace("Found ERDF profile " + XMLNS.ERDF);
      return true;
    } else {
      // otherwise fail check.
      //if(ERDF.log.isFatalEnabled())
      //	ERDF.log.fatal("No ERDF profile found.");
      return false;
    }
  },

  __stripHashes: function (s) {
    return s && typeof s.substring == 'function' && s.substring(0, 1) == '#' ? s.substring(1, s.length) : s;
  },

  registerSchema: function (prefix, namespace) {
    // TODO check whether already registered, if so, complain.
    ERDF.schemas.push({
      prefix: prefix,
      namespace: namespace,
    });

    //if(ERDF.log.isDebugEnabled())
    //	ERDF.log.debug("Prefix '"+prefix+"' for '"+namespace+"' registered.");
  },

  registerTriple: function (subject, predicate, object) {
    // if prefix is schema, this is a schema definition.
    if (predicate.prefix.toLowerCase() == 'schema') this.registerSchema(predicate.name, object.value);

    var triple = new ERDF.Triple(subject, predicate, object);
    ERDF.callback(triple);

    //if(ERDF.log.isInfoEnabled())
    //	ERDF.log.info(triple)

    // return the registered triple.
    return triple;
  },

  __enhanceObject: function () {
    /* Resource state querying methods */
    this.isResource = function () {
      return this.type == ERDF.RESOURCE;
    };
    this.isLocal = function () {
      return this.isResource() && this.value.startsWith('#');
    };
    this.isCurrentDocument = function () {
      return this.isResource() && this.value == '';
    };

    /* Resource getter methods.*/
    this.getId = function () {
      return this.isLocal() ? ERDF.__stripHashes(this.value) : false;
    };

    /* Liiteral state querying methods  */
    this.isLiteral = function () {
      return this.type == ERDF.LIITERAL;
    };
  },

  serialize: function (literal) {
    if (!literal) {
      return '';
    } else if (literal.constructor == String) {
      return literal;
    } else if (literal.constructor == Boolean) {
      return literal ? 'true' : 'false';
    } else {
      return literal.toString();
    }
  },
};

ERDF.Triple = function (subject, predicate, object) {
  this.subject = subject;
  this.predicate = predicate;
  this.object = object;

  this.toString = function () {
    return (
      '[ERDF.Triple] ' + this.subject.toString() + ' ' + this.predicate.prefix + ':' + this.predicate.name + ' ' + this.object.toString()
    );
  };
};

ERDF.Resource = function (uri) {
  this.type = ERDF.RESOURCE;
  this.value = uri;
  ERDF.__enhanceObject.apply(this);

  this.toString = function () {
    return '&lt;' + this.value + '&gt;';
  };
};

ERDF.Literal = function (literal) {
  this.type = ERDF.LITERAL;
  this.value = ERDF.serialize(literal);
  ERDF.__enhanceObject.apply(this);

  this.toString = function () {
    return '"' + this.value + '"';
  };
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/*
 * Save and triple generation behaviour. Use this area to configure
 * data management to your needs.
 */
var USE_ASYNCHRONOUS_REQUESTS = true;
var DISCARD_UNUSED_TRIPLES = true;
var PREFER_SPANS_OVER_DIVS = true;
var PREFER_TITLE_OVER_TEXTNODE = false;
var RESOURCE_ID_PREFIX = 'resource';

var SHOW_DEBUG_ALERTS_WHEN_SAVING = false;
var SHOW_EXTENDED_DEBUG_INFORMATION = false;

/*
 * Back end specific workarounds.
 */

var USE_ARESS_WORKAROUNDS = true;

/*
 * Data management constants. Do not change these, as they are used
 * both internally and externally to communicate on events and to identify
 * command object actions in triple production and embedding rules.
 */

// Resource constants
var RESOURCE_CREATED = 0x01;
var RESOURCE_REMOVED = 0x02;
var RESOURCE_SAVED = 0x04;
var RESOURCE_RELOADED = 0x08;
var RESOURCE_SYNCHRONIZED = 0x10;

// Triple constants
var TRIPLE_REMOVE = 0x01;
var TRIPLE_ADD = 0x02;
var TRIPLE_RELOAD = 0x04;
var TRIPLE_SAVE = 0x08;

var PROCESSDATA_REF = 'processdata';

// HTTP status code constants
//
//// 2xx
//const 200_OK =			'Ok';
//const 201_CREATED =		'Created';
//const 202_ACCEPTED =		'Accepted';
//const 204_NO_CONTENT =	'No Content';
//
//// 3xx
//const 301_MOVED_PERMANENTLY =	'Moved Permanently';
//const 302_MOVED_TEMPORARILY =	'Moved Temporarily';
//const 304_NOT_MODIFIED =		'Not Modified';
//
//// 4xx
//const 400_BAD_REQUEST =	'Bad Request';
//const 401_UNAUTHORIZED =	'Unauthorized';
//const 403_FORBIDDEN =		'Forbidden';
//const 404_NOT_FOUND =		'Not Found';
//const 409_CONFLICT =		'Conflict';
//
//// 5xx
//const 500_INTERNAL_SERVER_ERROR =		'Internal Server Error';
//const 501_NOT_IMPLEMENTED =			'Not Implemented';
//const 502_BAD_GATEWAY =				'Bad Gateway';
//const 503_SERVICE_UNAVAILABLE =		'Service Unavailable';
//
/**
 * The Data Management object. Use this one when interacting with page internal
 * data. Initialize data management by DataManager.init();
 * @class DataManager
 */
var DataManager = {
  /**
   * The init method should be called once in the DataManagers lifetime.
   * It causes the DataManager to initialize itself, the erdf parser, do all
   * neccessary registrations and configurations, to run the parser and
   * from then on deliver all resulting triples.
   * No parameters needed are needed in a call to this method.
   */
  init: function () {
    ERDF.init(DataManager._registerTriple);
    DataManager.__synclocal();
  },

  /**
   * This triple array is meant to be the whole knowledge of the DataManager.
   */
  _triples: [],

  /**
   * This method is meant for callback from erdf parsing. It is not to be
   * used in another way than to add triples to the triple store.
   * @param {Object} triple the triple to add to the triple store.
   */
  _registerTriple: function (triple) {
    DataManager._triples.push(triple);
  },

  /**
   * The __synclocal method is for internal usage only.
   * It performs synchronization with the local document, that is, the triple
   * store is adjustet to the content of the document, which could have been
   * changed by any other applications running on the same page.
   */
  __synclocal: function () {
    DataManager._triples = [];
    ERDF.run();
  },

  /**
   * Makes the shape passed into this method synchronize itself with the DOM.
   * This method returns the shapes resource object for further manipulation.
   * @param {Object} shape
   */
  __synchronizeShape: function (shape) {
    var r = ResourceManager.getResource(shape.resourceId);
    var serialize = shape.serialize();

    // store all serialize values
    serialize.each(function (ser) {
      var resource = ser.type == 'resource';
      var _triple = new ERDF.Triple(
        new ERDF.Resource(shape.resourceId),
        { prefix: ser.prefix, name: ser.name },
        resource ? new ERDF.Resource(ser.value) : new ERDF.Literal(ser.value)
      );
      DataManager.setObject(_triple);
    });

    return r;
  },

  __storeShape: function (shape) {
    // first synchronize the shape,
    var resource = DataManager.__synchronizeShape(shape);

    // then save the synchronized dom.
    resource.save();
  },

  __forceExistance: function (shape) {
    if (!$(shape.resourceId)) {
      if (!$$('.' + PROCESSDATA_REF)[0])
        DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
          'div',
          { class: PROCESSDATA_REF, style: 'display:none;' },
        ]);

      // object is literal
      DataManager.graft(XMLNS.XHTML, $$('.' + PROCESSDATA_REF)[0], [
        'div',
        {
          id: shape.resourceId,
          //This should be done in a more dynamic way!!!!!
          class: shape instanceof ORYX.Core.Canvas ? '-oryx-canvas' : undefined,
        },
      ]);
    } else {
      var resource = $(shape.resourceId);
      var children = $A(resource.childNodes);
      children.each(function (child) {
        resource.removeChild(child);
      });
    }
  },

  __persistShape: function (shape) {
    // a shape serialization.
    var shapeData = shape.serialize();

    // initialize a triple array and construct a shape resource
    // to be used in triple generation.
    var triplesArray = [];
    var shapeResource = new ERDF.Resource(shape.resourceId);

    // remove all triples for this particular shape's resource
    DataManager.removeTriples(DataManager.query(shapeResource, undefined, undefined));

    // for each data set in the shape's serialization
    shapeData.each(function (data) {
      // construct a triple's value
      var value = data.type == 'resource' ? new ERDF.Resource(data.value) : new ERDF.Literal(data.value);

      // construct triple and add it to the DOM.
      DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: data.prefix, name: data.name }, value));
    });
  },

  __persistDOM: function (facade) {
    // getChildShapes gets all shapes (nodes AND edges), deep flag
    // makes it return a flattened child hierarchy.

    var canvas = facade.getCanvas();
    var shapes = canvas.getChildShapes(true);
    var result = '';

    // persist all shapes.
    shapes.each(function (shape) {
      DataManager.__forceExistance(shape);
    });
    //DataManager.__synclocal();

    DataManager.__renderCanvas(facade);
    result += DataManager.serialize($(ERDF.__stripHashes(facade.getCanvas().resourceId)), true);

    shapes.each(function (shape) {
      DataManager.__persistShape(shape);
      result += DataManager.serialize($(ERDF.__stripHashes(shape.resourceId)), true);
    });

    //result += DataManager.__renderCanvas(facade);

    return result;
  },

  __renderCanvas: function (facade) {
    var canvas = facade.getCanvas();
    var stencilSets = facade.getStencilSets();
    var shapes = canvas.getChildShapes(true);

    DataManager.__forceExistance(canvas);

    DataManager.__persistShape(canvas);

    var shapeResource = new ERDF.Resource(canvas.resourceId);

    // remove all triples for this particular shape's resource
    DataManager.removeTriples(DataManager.query(shapeResource, undefined, undefined));

    DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'mode' }, new ERDF.Literal('writable')));

    DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'mode' }, new ERDF.Literal('fullscreen')));

    stencilSets.values().each(function (stencilset) {
      DataManager.addTriple(
        new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'stencilset' }, new ERDF.Resource(stencilset.source().replace(/&/g, '%26')))
      );

      DataManager.addTriple(
        new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'ssnamespace' }, new ERDF.Resource(stencilset.namespace()))
      );

      stencilset
        .extensions()
        .keys()
        .each(function (extension) {
          DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'ssextension' }, new ERDF.Literal(extension)));
        });
    });

    shapes.each(function (shape) {
      DataManager.addTriple(new ERDF.Triple(shapeResource, { prefix: 'oryx', name: 'render' }, new ERDF.Resource('#' + shape.resourceId)));
    });
  },

  __counter: 0,
  __provideId: function () {
    while ($(RESOURCE_ID_PREFIX + DataManager.__counter)) DataManager.__counter++;

    return RESOURCE_ID_PREFIX + DataManager.__counter;
  },

  serializeDOM: function (facade) {
    return DataManager.__persistDOM(facade);
  },

  syncGlobal: function (facade) {
    return DataManager.__syncglobal(facade);
  },

  /**
   * This method is used to synchronize local DOM with remote resources.
   * Local changes are commited to the server, and remote changes are
   * performed to the local document.
   * @param {Object} facade The facade of the editor that holds certain
   * resource representations as shapes.
   */
  __syncglobal: function (facade) {
    // getChildShapes gets all shapes (nodes AND edges), deep flag
    // makes it return a flattened child hierarchy.

    var canvas = facade.getCanvas();
    var shapes = canvas.getChildShapes(true);

    // create dummy resource representations in the dom
    // for all shapes that were newly created.

    shapes
      .select(function (shape) {
        // select shapes without resource id.

        return !$(shape.resourceId);
      })
      .each(function (shape) {
        // create new resources for them.
        if (USE_ARESS_WORKAROUNDS) {
          /*
           * This is a workaround due to a bug in aress. Resources are
           * ignoring changes to raziel:type property once they are
           * created. As long as this is not fixed, the resource is now
           * being created using a randomly guessed id, this temporary id
           * is then used in references and the appropriate div is being
           * populated with properties.
           *
           * AFTER THIS PHASE THE DATA IS INCONSISTENT AS REFERENCES POINT
           * TO IDS THAT ARE UNKNOWN TO THE BACK END.
           *
           * After the resource is actually created in aress, it gets an id
           * that is persistent. All shapes are then being populated with the
           * correct id references and stored on the server.
           *
           * AFTER THE SAVE PROCESS HAS RETURNED, THE DATA IS CONSISTENT
           * REGARDING THE ID REFERENCES AGAIN.
           */

          var razielType = shape.properties['raziel-type'];

          var div = '<div xmlns="http://www.w3.org/1999/xhtml">' + '<span class="raziel-type">' + razielType + '</span></div>';

          var r = ResourceManager.__createResource(div);
          shape.resourceId = r.id();
        } else {
          var r = ResourceManager.__createResource();
          shape.resourceId = r.id();
        }
      });

    shapes.each(function (shape) {
      // store all shapes.
      DataManager.__storeShape(shape);
    });
  },

  /**
   * This method serializes a single div into a string that satisfies the
   * client/server communication protocol. It ingnores all elements that have
   * an attribute named class that includes 'transient'.
   * @param {Object} node the element to serialize.
   * @param {Object} preserveNamespace whether to preserve the parent's
   *                 namespace. If you are not sure about namespaces, provide
   *                 just the element to be serialized.
   */
  serialize: function (node, preserveNamespace) {
    if (node.nodeType == node.ELEMENT_NODE) {
      // serialize an element node.

      var children = $A(node.childNodes);
      var attributes = $A(node.attributes);
      var clazz = new String(node.getAttribute('class'));
      var ignore = clazz.split(' ').member('transient');

      // ignore transients.

      if (ignore) return '';

      // start serialization.

      var result = '<' + node.nodeName;

      // preserve namespace?
      if (!preserveNamespace)
        result += ' xmlns="' + (node.namespaceURI ? node.namespaceURI : XMLNS.XHTML) + '" xmlns:oryx="http://oryx-editor.org"';

      // add all attributes.

      attributes.each(function (attribute) {
        result += ' ' + attribute.nodeName + '="' + attribute.nodeValue + '"';
      });

      // close if no children.

      if (children.length == 0) result += '/>';
      else {
        // serialize all children.

        result += '>';
        children.each(function (_node) {
          result += DataManager.serialize(_node, true);
        });
        result += '</' + node.nodeName + '>';
      }

      return result;
    } else if (node.nodeType == node.TEXT_NODE) {
      // serialize a text node.
      return node.nodeValue;
    }

    //TODO serialize cdata areas also.
    //TODO work on namespace awareness.
  },

  addTriple: function (triple) {
    // assert the subject is a resource

    if (!triple.subject.type == ERDF.LITERAL)
      throw 'Cannot add the triple ' + triple.toString() + ' because the subject is not a resource.';

    // get the element which represents this triple's subject.
    var elementId = ERDF.__stripHashes(triple.subject.value);
    var element = $(elementId);

    // assert the subject is inside this document.
    if (!element) throw 'Cannot add the triple ' + triple.toString() + ' because the subject "' + elementId + '" is not in the document.';

    if (triple.object.type == ERDF.LITERAL)
      // object is literal
      DataManager.graft(XMLNS.XHTML, element, [
        'span',
        { class: triple.predicate.prefix + '-' + triple.predicate.name },
        triple.object.value.escapeHTML(),
      ]);
    else {
      // object is resource
      DataManager.graft(XMLNS.XHTML, element, [
        'a',
        { rel: triple.predicate.prefix + '-' + triple.predicate.name, href: triple.object.value },
      ]);
    }

    return true;
  },

  removeTriples: function (triples) {
    // alert('Removing ' +triples.length+' triples.');

    // from all the triples select those ...
    var removed = triples.select(function (triple) {
      // TODO remove also from triple store.
      // ... that were actually removed.
      return DataManager.__removeTriple(triple);
    });

    // sync and return removed triples.
    // DataManager.__synclocal();
    return removed;
  },

  removeTriple: function (triple) {
    // remember whether the triple was actually removed.
    var result = DataManager.__removeTriple(triple);

    // sync and return removed triples.
    // DataManager.__synclocal();
    return result;
  },

  __removeTriple: function (triple) {
    // assert the subject is a resource
    if (!triple.subject.type == ERDF.LITERAL)
      throw 'Cannot remove the triple ' + triple.toString() + ' because the subject is not a resource.';

    // get the element which represents this triple's subject.
    var elementId = ERDF.__stripHashes(triple.subject.value);
    var element = $(elementId);

    // assert the subject is inside this document.
    if (!element) throw 'Cannot remove the triple ' + triple.toString() + ' because the subject is not in the document.';

    if (triple.object.type == ERDF.LITERAL) {
      // continue searching actively for the triple.
      var result = DataManager.__removeTripleRecursively(triple, element);
      return result;
    }
  },

  __removeTripleRecursively: function (triple, continueFrom) {
    // return when this node is not an element node.
    if (continueFrom.nodeType != continueFrom.ELEMENT_NODE) return false;

    var classes = new String(continueFrom.getAttribute('class'));
    var children = $A(continueFrom.childNodes);

    if (classes.include(triple.predicate.prefix + '-' + triple.predicate.name)) {
      var content = continueFrom.textContent;
      if (triple.object.type == ERDF.LITERAL && triple.object.value == content) continueFrom.parentNode.removeChild(continueFrom);

      return true;
    } else {
      children.each(function (_node) {
        DataManager.__removeTripleRecursively(triple, _node);
      });
      return false;
    }
  },

  /**
   * graft() function
   * Originally by Sean M. Burke from interglacial.com, altered for usage with
   * SVG and namespace (xmlns) support. Be sure you understand xmlns before
   * using this funtion, as it creates all grafted elements in the xmlns
   * provided by you and all element's attribures in default xmlns. If you
   * need to graft elements in a certain xmlns and wish to assign attributes
   * in both that and another xmlns, you will need to do stepwise grafting,
   * adding non-default attributes yourself or you'll have to enhance this
   * function. Latter, I would appreciate: martin???apfelfabrik.de
   * @param {Object} namespace The namespace in which
   * 					elements should be grafted.
   * @param {Object} parent The element that should contain the grafted
   * 					structure after the function returned.
   * @param {Object} t the crafting structure.
   * @param {Object} doc the document in which grafting is performed.
   */
  graft: function (namespace, parent, t, doc) {
    doc = doc || (parent && parent.ownerDocument) || document;
    var e;
    if (t === undefined) {
      echo("Can't graft an undefined value");
    } else if (t.constructor == String) {
      e = doc.createTextNode(t);
    } else {
      for (var i = 0; i < t.length; i++) {
        if (i === 0 && t[i].constructor == String) {
          var snared = t[i].match(/^([a-z][a-z0-9]*)\.([^\s\.]+)$/i);
          if (snared) {
            e = doc.createElementNS(namespace, snared[1]);
            e.setAttributeNS(null, 'class', snared[2]);
            continue;
          }
          snared = t[i].match(/^([a-z][a-z0-9]*)$/i);
          if (snared) {
            e = doc.createElementNS(namespace, snared[1]); // but no class
            continue;
          }

          // Otherwise:
          e = doc.createElementNS(namespace, 'span');
          e.setAttribute(null, 'class', 'namelessFromLOL');
        }

        if (t[i] === undefined) {
          echo("Can't graft an undefined value in a list!");
        } else if (t[i].constructor == String || t[i].constructor == Array) {
          this.graft(namespace, e, t[i], doc);
        } else if (t[i].constructor == Number) {
          this.graft(namespace, e, t[i].toString(), doc);
        } else if (t[i].constructor == Object) {
          // hash's properties => element's attributes
          for (var k in t[i]) {
            e.setAttributeNS(null, k, t[i][k]);
          }
        } else if (t[i].constructor == Boolean) {
          this.graft(namespace, e, t[i] ? 'true' : 'false', doc);
        } else throw 'Object ' + t[i] + ' is inscrutable as an graft arglet.';
      }
    }

    if (parent) parent.appendChild(e);

    return Element.extend(e); // return the topmost created node
  },

  setObject: function (triple) {
    /**
     * Erwartungen von Arvid an diese Funktion:
     * - Es existiert genau ein triple mit dem Subjekt und Praedikat,
     *   das uebergeben wurde, und dieses haelt uebergebenes Objekt.
     */

    var triples = DataManager.query(triple.subject, triple.predicate, undefined);

    DataManager.removeTriples(triples);

    DataManager.addTriple(triple);

    return true;
  },

  query: function (subject, predicate, object) {
    /*
     * Typical triple.
     *	{value: subject, type: subjectType},
     *	{prefix: schema.prefix, name: property},
     *	{value: object, type: objectType});
     */

    return DataManager._triples.select(function (triple) {
      var select = subject ? triple.subject.type == subject.type && triple.subject.value == subject.value : true;
      if (predicate) {
        select = select && (predicate.prefix ? triple.predicate.prefix == predicate.prefix : true);
        select = select && (predicate.name ? triple.predicate.name == predicate.name : true);
      }
      select = select && (object ? triple.object.type == object.type && triple.object.value == object.value : true);
      return select;
    });
  },
};

Kickstart.register(DataManager.init);

function assert(expr, m) {
  if (!expr) throw m;
}

function DMCommand(action, triple) {
  // store action and triple.
  this.action = action;
  this.triple = triple;

  this.toString = function () {
    return 'Command(' + action + ', ' + triple + ')';
  };
}

function DMCommandHandler(nextHandler) {
  /**
   * Private method to set the next handler in the Chain of Responsibility
   * (see http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern for
   * details).
   * @param {DMCommandHandler} handler The handler that is next in the chain.
   */
  this.__setNext = function (handler) {
    var _next = this.__next;
    this.__next = nextHandler;
    return _next ? _next : true;
  };
  this.__setNext(nextHandler);

  /**
   * Invokes the next handler. If there is no next handler, this method
   * returns false, otherwise it forwards the result of the handling.
   * @param {Object} command The command object to be processed.
   */
  this.__invokeNext = function (command) {
    return this.__next ? this.__next.handle(command) : false;
  };

  /**
   * Handles a command. The abstract method process() is called with the
   * command object that has been passed. If the process method catches the
   * command (returns true on completion), the handle() method returns true.
   * If the process() method doesn't catch the command, the next handler will
   * be invoked.
   * @param {Object} command The command object to be processed.
   */
  this.handle = function (command) {
    return this.process(command) ? true : this.__invokeNext(command);
  };

  /**
   * Empty process() method returning false. If javascript knew abstract
   * class members, this would be one.
   * @param {Object} command The command object to process.
   */
  this.process = function (command) {
    return false;
  };
}

/**
 * This Handler manages the addition and the removal of meta elements in the
 * head of the document.
 * @param {DMCommandHandler} next The handler that is next in the chain.
 */
function MetaTagHandler(next) {
  DMCommandHandler.apply(this, [next]);
  this.process = function (command) {
    with (command.triple) {
      /* assert prerequisites */
      if (!(subject instanceof ERDF.Resource && subject.isCurrentDocument() && object instanceof ERDF.Literal)) return false;
    }
  };
}

var chain = new MetaTagHandler();
var command = new DMCommand(TRIPLE_ADD, new ERDF.Triple(new ERDF.Resource(''), 'rdf:tool', new ERDF.Literal('')));

/*
if(chain.handle(command))
	alert('Handled!');
*/

ResourceManager = {
  __corrupt: false,
  __latelyCreatedResource: undefined,
  __listeners: $H(),
  __token: 1,

  addListener: function (listener, mask) {
    if (!(listener instanceof Function)) throw 'Resource event listener is not a function!';
    if (!mask) throw 'Invalid mask for resource event listener registration.';

    // construct controller and token.
    var controller = { listener: listener, mask: mask };
    var token = ResourceManager.__token++;

    // add new listener.
    ResourceManager.__listeners[token] = controller;

    // return the token generated.
    return token;
  },

  removeListener: function (token) {
    // remove the listener with the token and return it.
    return ResourceManager.__listners.remove(token);
  },

  __Event: function (action, resourceId) {
    this.action = action;
    this.resourceId = resourceId;
  },

  __dispatchEvent: function (event) {
    // get all listeners. for each listener, ...
    ResourceManager.__listeners.values().each(function (controller) {
      // .. if listener subscribed to this type of event ...
      if (event.action & controller.mask) return controller.listener(event);
    });
  },

  getResource: function (id) {
    // get all possible resources for this.
    id = ERDF.__stripHashes(id);
    var resources = DataManager.query(new ERDF.Resource('#' + id), { prefix: 'raziel', name: 'entry' }, undefined);

    // check for consistency.
    if (resources.length == 1 && resources[0].object.isResource()) {
      var entryUrl = resources[0].object.value;
      return new ResourceManager.__Resource(id, entryUrl);
    }

    // else throw an error message.
    throw (
      'Resource with id ' +
      id +
      ' not recognized as such. ' +
      (resources.length > 1 ? ' There is more than one raziel:entry URL.' : ' There is no raziel:entry URL.')
    );

    return false;
  },

  __createResource: function (alternativeDiv) {
    var collectionUrls = DataManager.query(
      new ERDF.Resource(''),
      // TODO This will become raziel:collection in near future.
      { prefix: 'raziel', name: 'collection' },
      undefined
    );

    // check for consistency.

    if (collectionUrls.length == 1 && collectionUrls[0].object.isResource()) {
      // get the collection url.

      var collectionUrl = collectionUrls[0].object.value;
      var resource = undefined;

      // if there is an old id, serialize the dummy div from there,
      // otherwise create a dummy div on the fly.

      var serialization = alternativeDiv ? alternativeDiv : '<div xmlns="http://www.w3.org/1999/xhtml"></div>';

      ResourceManager.__request(
        'POST',
        collectionUrl,
        serialization,

        // on success
        function () {
          // get div and id that have been generated by the server.

          var response = this.responseXML;
          var div = response.childNodes[0];
          var id = div.getAttribute('id');

          // store div in DOM
          if (!$$('.' + PROCESSDATA_REF)[0])
            DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
              'div',
              { class: PROCESSDATA_REF, style: 'display:none;' },
            ]);

          $$('.' + PROCESSDATA_REF)[0].appendChild(div.cloneNode(true));

          // parse local erdf data once more.

          DataManager.__synclocal();

          // get new resource object.

          resource = new ResourceManager.getResource(id);

          // set up an action informing of the creation.

          ResourceManager.__resourceActionSucceeded(this, RESOURCE_CREATED, undefined);
        },

        function () {
          ResourceManager.__resourceActionFailed(this, RESOURCE_CREATED, undefined);
        },
        false
      );

      return resource;
    }

    // else
    throw 'Could not create resource! raziel:collection URL is missing!';
    return false;
  },

  __Resource: function (id, url) {
    this.__id = id;
    this.__url = url;

    /*
     * Process URL is no longer needed to refer to the shape element on the
     * canvas. AReSS uses the id's to gather information on fireing
     * behaviour now.
     */

    //		// find the process url.
    //		var processUrl = undefined;
    //
    //		var urls = DataManager.query(
    //			new ERDF.Resource('#'+this.__id),
    //			{prefix: 'raziel', name: 'process'},
    //			undefined
    //		);
    //
    //		if(urls.length == 0) { throw 'The resource with the id ' +id+ ' has no process url.'};
    //
    //		urls.each( function(triple) {
    //
    //			// if there are more urls, use the last one.
    //			processUrl = triple.object.value;
    //		});
    //
    //		this.__processUrl = processUrl;
    //
    //		// convenience function for getting the process url.
    //		this.processUrl = function() {
    //			return this.__processUrl;
    //		}

    // convenience finction for getting the id.
    this.id = function () {
      return this.__id;
    };

    // convenience finction for getting the entry url.
    this.url = function () {
      return this.__url;
    };

    this.reload = function () {
      var _url = this.__url;
      var _id = this.__id;
      ResourceManager.__request(
        'GET',
        _url,
        null,
        function () {
          ResourceManager.__resourceActionSucceeded(this, RESOURCE_RELOADED, _id);
        },
        function () {
          ResourceManager.__resourceActionFailed(this, RESURCE_RELOADED, _id);
        },
        USE_ASYNCHRONOUS_REQUESTS
      );
    };

    this.save = function (synchronize) {
      var _url = this.__url;
      var _id = this.__id;
      data = DataManager.serialize($(_id));
      ResourceManager.__request(
        'PUT',
        _url,
        data,
        function () {
          ResourceManager.__resourceActionSucceeded(this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE_SAVED, _id);
        },
        function () {
          ResourceManager.__resourceActionFailed(this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE.SAVED, _id);
        },
        USE_ASYNCHRONOUS_REQUESTS
      );
    };

    this.remove = function () {
      var _url = this.__url;
      var _id = this.__id;
      ResourceManager.__request(
        'DELETE',
        _url,
        null,
        function () {
          ResourceManager.__resourceActionSucceeded(this, RESOURCE_REMOVED, _id);
        },
        function () {
          ResourceManager.__resourceActionFailed(this, RESOURCE_REMOVED, _id);
        },
        USE_ASYNCHRONOUS_REQUESTS
      );
    };
  },

  request: function (url, requestOptions) {
    var options = {
      method: 'get',
      asynchronous: true,
      parameters: {},
    };

    Object.extend(options, requestOptions || {});

    var params = Hash.toQueryString(options.parameters);
    if (params) url += (url.include('?') ? '&' : '?') + params;

    return ResourceManager.__request(
      options.method,
      url,
      options.data,
      options.onSuccess instanceof Function
        ? function () {
            options.onSuccess(this);
          }
        : undefined,
      options.onFailure instanceof Function
        ? function () {
            options.onFailure(this);
          }
        : undefined,
      options.asynchronous && USE_ASYNCHRONOUS_REQUESTS,
      options.headers
    );
  },

  __request: function (method, url, data, success, error, async, headers) {
    // get a request object
    var httpRequest = Try.these(
      /* do the Mozilla/Safari/Opera stuff */
      function () {
        return new XMLHttpRequest();
      },

      /* do the IE stuff */
      function () {
        return new ActiveXObject('Msxml2.XMLHTTP');
      },
      function () {
        return new ActiveXObject('Microsoft.XMLHTTP');
      }
    );

    // if there is no request object ...
    if (!httpRequest) {
      if (!this.__corrupt)
        throw 'This browser does not provide any AJAX functionality. You will not be able to use the software provided with the page you are viewing. Please consider installing appropriate extensions.';
      this.__corrupt = true;
      return false;
    }

    if (success instanceof Function) httpRequest.onload = success;
    if (error instanceof Function) {
      httpRequest.onerror = error;
    }

    var h = $H(headers);
    h.keys().each(function (key) {
      httpRequest.setRequestHeader(key, h[key]);
    });

    try {
      if (SHOW_DEBUG_ALERTS_WHEN_SAVING) alert(method + ' ' + url + '\n' + SHOW_EXTENDED_DEBUG_INFORMATION ? data : '');

      // TODO Remove synchronous calls to the server as soon as xenodot
      // handles asynchronous requests without failure.
      httpRequest.open(method, url, !async ? false : true);
      httpRequest.send(data);
    } catch (e) {
      return false;
    }
    return true;
  },

  __resourceActionSucceeded: function (transport, action, id) {
    var status = transport.status;
    var response = transport.responseText;

    if (SHOW_DEBUG_ALERTS_WHEN_SAVING) alert(status + ' ' + url + '\n' + SHOW_EXTENDED_DEBUG_INFORMATION ? data : '');

    // if the status code is not in 2xx, throw an error.
    if (status >= 300)
      throw (
        'The server responded with an error: ' +
        status +
        '\n' +
        (SHOW_EXTENDED_DEBUG_INFORMATION
          ? +data
          : 'If you need additional information here, including the data sent by the server, consider setting SHOW_EXTENDED_DEBUG_INFORMATION to true.')
      );

    switch (action) {
      case RESOURCE_REMOVED:
        // get div and id
        var response = transport.responseXML;
        var div = response.childNodes[0];
        var id = div.getAttribute('id');

        // remove the resource from DOM
        var localDiv = document.getElementById(id);
        localDiv.parentNode.removeChild(localDiv);
        break;

      case RESOURCE_CREATED:
        // nothing remains to be done.
        break;

      case RESOURCE_SAVED | RESOURCE_SYNCHRONIZED:
        DataManager.__synclocal();

      case RESOURCE_SAVED:
        // nothing remains to be done.
        break;

      case RESOURCE_RELOADED:
        // get div and id
        var response = transport.responseXML;
        var div = response.childNodes[0];
        var id = div.getAttribute('id');

        // remove the local resource representation from DOM
        var localDiv = document.getElementById(id);
        localDiv.parentNode.removeChild(localDiv);

        // store div in DOM
        if (!$$(PROCESSDATA_REF)[0])
          DataManager.graft(XMLNS.XHTML, document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), [
            'div',
            { class: PROCESSDATA_REF, style: 'display:none;' },
          ]);

        $$(PROCESSDATA_REF)[0].appendChild(div.cloneNode(true));
        DataManager.__synclocal();
        break;

      default:
        DataManager.__synclocal();
    }

    // dispatch to all listeners ...
    ResourceManager.__dispatchEvent(
      // ... an event describing the change that happened here.
      new ResourceManager.__Event(action, id)
    );
  },

  __resourceActionFailed: function (transport, action, id) {
    throw (
      'Fatal: Resource action failed. There is something horribly ' +
      'wrong with either the server, the transport protocol or your ' +
      "online status. Sure you're online?"
    );
  },
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * The super class for all classes in ORYX. Adds some OOP feeling to javascript.
 * See article "Object Oriented Super Class Method Calling with JavaScript" on
 * http://truecode.blogspot.com/2006/08/object-oriented-super-class-method.html
 * for a documentation on this. Fairly good article that points out errors in
 * Douglas Crockford's inheritance and super method calling approach.
 * Worth reading.
 * @class Clazz
 */
var Clazz = function () {};

/**
 * Empty constructor.
 * @methodOf Clazz.prototype
 */
Clazz.prototype.construct = function () {};

/**
 * Can be used to build up inheritances of classes.
 * @example
 * var MyClass = Clazz.extend({
 *   construct: function(myParam){
 *     // Do sth.
 *   }
 * });
 * var MySubClass = MyClass.extend({
 *   construct: function(myParam){
 *     // Use this to call constructor of super class
 *     arguments.callee.$.construct.apply(this, arguments);
 *     // Do sth.
 *   }
 * });
 * @param {Object} def The definition of the new class.
 */
Clazz.extend = function (def) {
  var classDef = function () {
    if (arguments[0] !== Clazz) {
      this.construct.apply(this, arguments);
    }
  };

  var proto = new this(Clazz);
  var superClass = this.prototype;

  for (var n in def) {
    var item = def[n];
    if (item instanceof Function) item.$ = superClass;
    proto[n] = item;
  }

  classDef.prototype = proto;

  //Give this new class the same static extend method
  classDef.extend = this.extend;
  return classDef;
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX) var ORYX = {};

if (!ORYX.CONFIG) ORYX.CONFIG = {};

/**
 * This file contains URI constants that may be used for XMLHTTPRequests.
 */

ORYX.CONFIG.ROOT_PATH = 'editor/'; //TODO: Remove last slash!!
ORYX.CONFIG.EXPLORER_PATH = 'explorer';
ORYX.CONFIG.LIBS_PATH = 'libs';

/**
 * Regular Config
 */
ORYX.CONFIG.SERVER_HANDLER_ROOT = 'service';
ORYX.CONFIG.SERVER_EDITOR_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor';
ORYX.CONFIG.SERVER_MODEL_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/model';
ORYX.CONFIG.STENCILSET_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_stencilset?embedsvg=true&url=true&namespace=';
ORYX.CONFIG.STENCIL_SETS_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_stencilset';

ORYX.CONFIG.PLUGINS_CONFIG = 'editor-app/plugins.xml';
ORYX.CONFIG.SYNTAXCHECKER_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/syntaxchecker';
ORYX.CONFIG.DEPLOY_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/model/deploy';
ORYX.CONFIG.MODEL_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/models';
ORYX.CONFIG.FORM_FLOW_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/formflows';
ORYX.CONFIG.FORM_FLOW_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/formflow';
ORYX.CONFIG.FORM_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/forms';
ORYX.CONFIG.FORM_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/form';
ORYX.CONFIG.SUB_PROCESS_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/subprocesses';
ORYX.CONFIG.SUB_PROCESS_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/subprocess';
ORYX.CONFIG.TEST_SERVICE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/service/';

ORYX.CONFIG.SERVICE_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/services';
ORYX.CONFIG.CONDITION_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/conditionelements';
ORYX.CONFIG.VARIABLEDEF_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/variabledefinitionelements';
ORYX.CONFIG.VALIDATOR_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/validators';

ORYX.CONFIG.SS_EXTENSIONS_FOLDER = ORYX.CONFIG.ROOT_PATH + 'stencilsets/extensions/';
ORYX.CONFIG.SS_EXTENSIONS_CONFIG = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/editor_ssextensions';
ORYX.CONFIG.ORYX_NEW_URL = '/new';
ORYX.CONFIG.BPMN_LAYOUTER = ORYX.CONFIG.ROOT_PATH + 'bpmnlayouter';

ORYX.CONFIG.EXPRESSION_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/expression-metadata';
ORYX.CONFIG.DATASOURCE_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + '/datasource-metadata';
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX) var ORYX = {};

if (!ORYX.CONFIG) ORYX.CONFIG = {};

/**
 * Signavio specific variables
 */
ORYX.CONFIG.BACKEND_SWITCH = true;
ORYX.CONFIG.PANEL_LEFT_WIDTH = 250;
ORYX.CONFIG.PANEL_RIGHT_COLLAPSED = true;
ORYX.CONFIG.PANEL_RIGHT_WIDTH = 300;
ORYX.CONFIG.APPNAME = 'KISBPM';
ORYX.CONFIG.WEB_URL = '.';

ORYX.CONFIG.BLANK_IMAGE = ORYX.CONFIG.LIBS_PATH + '/ext-2.0.2/resources/images/default/s.gif';

/* Specify offset of header */
ORYX.CONFIG.OFFSET_HEADER = 61;

/* Show grid line while dragging */
ORYX.CONFIG.SHOW_GRIDLINE = true;

/* Editor-Mode */
ORYX.CONFIG.MODE_READONLY = 'readonly';
ORYX.CONFIG.MODE_FULLSCREEN = 'fullscreen';
ORYX.CONFIG.WINDOW_HEIGHT = 800;
ORYX.CONFIG.PREVENT_LOADINGMASK_AT_READY = false;

/* Plugins */
ORYX.CONFIG.PLUGINS_ENABLED = true;
ORYX.CONFIG.PLUGINS_FOLDER = 'Plugins/';

ORYX.CONFIG.BPMN20_SCHEMA_VALIDATION_ON = true;

/* Namespaces */
ORYX.CONFIG.NAMESPACE_ORYX = 'http://www.b3mn.org/oryx';
ORYX.CONFIG.NAMESPACE_SVG = 'http://www.w3.org/2000/svg';

/* UI */
ORYX.CONFIG.CANVAS_WIDTH = 1200;
ORYX.CONFIG.CANVAS_HEIGHT = 1050;
ORYX.CONFIG.CANVAS_RESIZE_INTERVAL = 100;
ORYX.CONFIG.CANVAS_MIN_WIDTH = 800;
ORYX.CONFIG.CANVAS_MIN_HEIGHT = 300;
ORYX.CONFIG.SELECTED_AREA_PADDING = 4;
ORYX.CONFIG.CANVAS_BACKGROUND_COLOR = 'none';
ORYX.CONFIG.GRID_DISTANCE = 30;
ORYX.CONFIG.GRID_ENABLED = true;
ORYX.CONFIG.ZOOM_OFFSET = 0.1;
ORYX.CONFIG.DEFAULT_SHAPE_MARGIN = 60;
ORYX.CONFIG.SCALERS_SIZE = 7;
ORYX.CONFIG.MINIMUM_SIZE = 20;
ORYX.CONFIG.MAXIMUM_SIZE = 10000;
ORYX.CONFIG.OFFSET_MAGNET = 15;
ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP = 8;
ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM = 8;
ORYX.CONFIG.OFFSET_EDGE_BOUNDS = 5;
ORYX.CONFIG.COPY_MOVE_OFFSET = 30;

ORYX.CONFIG.BORDER_OFFSET = 14;

ORYX.CONFIG.MAX_NUM_SHAPES_NO_GROUP = 20; // Updated so the form editor shows all elements at once

ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER = 30;
ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET = 45;

/* Shape-Menu Align */
ORYX.CONFIG.SHAPEMENU_RIGHT = 'Oryx_Right';
ORYX.CONFIG.SHAPEMENU_BOTTOM = 'Oryx_Bottom';
ORYX.CONFIG.SHAPEMENU_LEFT = 'Oryx_Left';
ORYX.CONFIG.SHAPEMENU_TOP = 'Oryx_Top';

/* Morph-Menu Item */
ORYX.CONFIG.MORPHITEM_DISABLED = 'Oryx_MorphItem_disabled';

/* Property type names */
ORYX.CONFIG.TYPE_STRING = 'string';
ORYX.CONFIG.TYPE_BOOLEAN = 'boolean';
ORYX.CONFIG.TYPE_INTEGER = 'integer';
ORYX.CONFIG.TYPE_FLOAT = 'float';
ORYX.CONFIG.TYPE_COLOR = 'color';
ORYX.CONFIG.TYPE_DATE = 'date';
ORYX.CONFIG.TYPE_CHOICE = 'choice';
ORYX.CONFIG.TYPE_URL = 'url';
ORYX.CONFIG.TYPE_DIAGRAM_LINK = 'diagramlink';
ORYX.CONFIG.TYPE_COMPLEX = 'complex';
ORYX.CONFIG.TYPE_MULTIPLECOMPLEX = 'multiplecomplex';
ORYX.CONFIG.TYPE_TEXT = 'text';
ORYX.CONFIG.TYPE_KISBPM_MULTIINSTANCE = 'kisbpm-multiinstance';
ORYX.CONFIG.TYPE_MODEL_LINK = 'modellink';
ORYX.CONFIG.TYPE_FORM_FLOW_LINK = 'formflowlink';
ORYX.CONFIG.TYPE_FORM_LINK = 'formlink';
ORYX.CONFIG.TYPE_SUB_PROCESS_LINK = 'subprocesslink';
ORYX.CONFIG.TYPE_SERVICE_LINK = 'servicelink';
ORYX.CONFIG.TYPE_CONDITIONS = 'conditions';
ORYX.CONFIG.TYPE_VARIABLES = 'variables';
ORYX.CONFIG.TYPE_LISTENER = 'listener';
ORYX.CONFIG.TYPE_EPC_FREQ = 'epcfrequency';
ORYX.CONFIG.TYPE_GLOSSARY_LINK = 'glossarylink';
ORYX.CONFIG.TYPE_EXPRESSION = 'expression';
ORYX.CONFIG.TYPE_DATASOURCE = 'datasource';
ORYX.CONFIG.TYPE_DATASOURCE_MINIMAL = 'datasource-minimal';
ORYX.CONFIG.TYPE_VALIDATORS = 'validators';

/* Vertical line distance of multiline labels */
ORYX.CONFIG.LABEL_LINE_DISTANCE = 2;
ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT = 12;

/* Open Morph Menu with Hover */
ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER = false;

/* Editor constants come here */
ORYX.CONFIG.EDITOR_ALIGN_BOTTOM = 0x01;
ORYX.CONFIG.EDITOR_ALIGN_MIDDLE = 0x02;
ORYX.CONFIG.EDITOR_ALIGN_TOP = 0x04;
ORYX.CONFIG.EDITOR_ALIGN_LEFT = 0x08;
ORYX.CONFIG.EDITOR_ALIGN_CENTER = 0x10;
ORYX.CONFIG.EDITOR_ALIGN_RIGHT = 0x20;
ORYX.CONFIG.EDITOR_ALIGN_SIZE = 0x30;

/* Event types */
ORYX.CONFIG.EVENT_MOUSEDOWN = 'mousedown';
ORYX.CONFIG.EVENT_MOUSEUP = 'mouseup';
ORYX.CONFIG.EVENT_MOUSEOVER = 'mouseover';
ORYX.CONFIG.EVENT_MOUSEOUT = 'mouseout';
ORYX.CONFIG.EVENT_MOUSEMOVE = 'mousemove';
ORYX.CONFIG.EVENT_DBLCLICK = 'dblclick';
ORYX.CONFIG.EVENT_KEYDOWN = 'keydown';
ORYX.CONFIG.EVENT_KEYUP = 'keyup';

ORYX.CONFIG.EVENT_LOADED = 'editorloaded';
ORYX.CONFIG.EVENT_SAVED = 'editorSaved';

ORYX.CONFIG.EVENT_EXECUTE_COMMANDS = 'executeCommands';
ORYX.CONFIG.EVENT_STENCIL_SET_LOADED = 'stencilSetLoaded';
ORYX.CONFIG.EVENT_SELECTION_CHANGED = 'selectionchanged';
ORYX.CONFIG.EVENT_SHAPEADDED = 'shapeadded';
ORYX.CONFIG.EVENT_SHAPEREMOVED = 'shaperemoved';
ORYX.CONFIG.EVENT_PROPERTY_CHANGED = 'propertyChanged';
ORYX.CONFIG.EVENT_DRAGDROP_START = 'dragdrop.start';
ORYX.CONFIG.EVENT_SHAPE_MENU_CLOSE = 'shape.menu.close';
ORYX.CONFIG.EVENT_DRAGDROP_END = 'dragdrop.end';
ORYX.CONFIG.EVENT_RESIZE_START = 'resize.start';
ORYX.CONFIG.EVENT_RESIZE_END = 'resize.end';
ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED = 'dragDocker.docked';
ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW = 'highlight.showHighlight';
ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE = 'highlight.hideHighlight';
ORYX.CONFIG.EVENT_LOADING_ENABLE = 'loading.enable';
ORYX.CONFIG.EVENT_LOADING_DISABLE = 'loading.disable';
ORYX.CONFIG.EVENT_LOADING_STATUS = 'loading.status';
ORYX.CONFIG.EVENT_OVERLAY_SHOW = 'overlay.show';
ORYX.CONFIG.EVENT_OVERLAY_HIDE = 'overlay.hide';
ORYX.CONFIG.EVENT_ARRANGEMENT_TOP = 'arrangement.setToTop';
ORYX.CONFIG.EVENT_ARRANGEMENT_BACK = 'arrangement.setToBack';
ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD = 'arrangement.setForward';
ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD = 'arrangement.setBackward';
ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED = 'propertyWindow.propertyChanged';
ORYX.CONFIG.EVENT_LAYOUT_ROWS = 'layout.rows';
ORYX.CONFIG.EVENT_LAYOUT_BPEL = 'layout.BPEL';
ORYX.CONFIG.EVENT_LAYOUT_BPEL_VERTICAL = 'layout.BPEL.vertical';
ORYX.CONFIG.EVENT_LAYOUT_BPEL_HORIZONTAL = 'layout.BPEL.horizontal';
ORYX.CONFIG.EVENT_LAYOUT_BPEL_SINGLECHILD = 'layout.BPEL.singlechild';
ORYX.CONFIG.EVENT_LAYOUT_BPEL_AUTORESIZE = 'layout.BPEL.autoresize';
ORYX.CONFIG.EVENT_AUTOLAYOUT_LAYOUT = 'autolayout.layout';
ORYX.CONFIG.EVENT_UNDO_EXECUTE = 'undo.execute';
ORYX.CONFIG.EVENT_UNDO_ROLLBACK = 'undo.rollback';
ORYX.CONFIG.EVENT_BUTTON_UPDATE = 'toolbar.button.update';
ORYX.CONFIG.EVENT_LAYOUT = 'layout.dolayout';
ORYX.CONFIG.EVENT_GLOSSARY_LINK_EDIT = 'glossary.link.edit';
ORYX.CONFIG.EVENT_GLOSSARY_SHOW = 'glossary.show.info';
ORYX.CONFIG.EVENT_GLOSSARY_NEW = 'glossary.show.new';
ORYX.CONFIG.EVENT_DOCKERDRAG = 'dragTheDocker';
ORYX.CONFIG.EVENT_CANVAS_SCROLL = 'canvas.scroll';

ORYX.CONFIG.EVENT_SHOW_PROPERTYWINDOW = 'propertywindow.show';
ORYX.CONFIG.EVENT_ABOUT_TO_SAVE = 'file.aboutToSave';

/* Selection Shapes Highlights */
ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE = 5;
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR = '#4444FF';
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR2 = '#9999FF';

ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_CORNER = 'corner';
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE = 'rectangle';

ORYX.CONFIG.SELECTION_VALID_COLOR = '#00FF00';
ORYX.CONFIG.SELECTION_INVALID_COLOR = '#FF0000';

ORYX.CONFIG.DOCKER_DOCKED_COLOR = '#00FF00';
ORYX.CONFIG.DOCKER_UNDOCKED_COLOR = '#FF0000';
ORYX.CONFIG.DOCKER_SNAP_OFFSET = 10;

/* Copy & Paste */
ORYX.CONFIG.EDIT_OFFSET_PASTE = 10;

/* Key-Codes */
ORYX.CONFIG.KEY_CODE_X = 88;
ORYX.CONFIG.KEY_CODE_C = 67;
ORYX.CONFIG.KEY_CODE_V = 86;
ORYX.CONFIG.KEY_CODE_DELETE = 46;
ORYX.CONFIG.KEY_CODE_META = 224;
ORYX.CONFIG.KEY_CODE_BACKSPACE = 8;
ORYX.CONFIG.KEY_CODE_LEFT = 37;
ORYX.CONFIG.KEY_CODE_RIGHT = 39;
ORYX.CONFIG.KEY_CODE_UP = 38;
ORYX.CONFIG.KEY_CODE_DOWN = 40;

// TODO Determine where the lowercase constants are still used and remove them from here.
ORYX.CONFIG.KEY_Code_enter = 12;
ORYX.CONFIG.KEY_Code_left = 37;
ORYX.CONFIG.KEY_Code_right = 39;
ORYX.CONFIG.KEY_Code_top = 38;
ORYX.CONFIG.KEY_Code_bottom = 40;

/* Supported Meta Keys */

ORYX.CONFIG.META_KEY_META_CTRL = 'metactrl';
ORYX.CONFIG.META_KEY_ALT = 'alt';
ORYX.CONFIG.META_KEY_SHIFT = 'shift';

/* Key Actions */

ORYX.CONFIG.KEY_ACTION_DOWN = 'down';
ORYX.CONFIG.KEY_ACTION_UP = 'up';

/* Form Rowlayouting */
ORYX.CONFIG.FORM_ROW_WIDTH = 350;
ORYX.CONFIG.FORM_GROUP_MARGIN = 5;
ORYX.CONFIG.FORM_GROUP_EMPTY_HEIGHT = 100;

/* Form element types */
ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX = 'http://b3mn.org/stencilset/xforms';
ORYX.CONFIG.FORM_ELEMENT_TYPE_ROOT = 'http://b3mn.org/stencilset/xforms#XForm';
ORYX.CONFIG.FORM_ELEMENT_TYPE_GROUP = 'http://b3mn.org/stencilset/xforms#Group';
ORYX.CONFIG.FORM_ELEMENT_TYPE_REPEATING_GROUP = 'http://b3mn.org/stencilset/xforms#RepeatingGroup';
ORYX.CONFIG.FORM_ELEMENT_TYPE_LABEL_FIELD = 'http://b3mn.org/stencilset/xforms#LabelField';

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

function printf() {
  var result = arguments[0];
  for (var i = 1; i < arguments.length; i++) result = result.replace('%' + (i - 1), arguments[i]);
  return result;
}

// oryx constants.
var ORYX_LOGLEVEL_TRACE = 5;
var ORYX_LOGLEVEL_DEBUG = 4;
var ORYX_LOGLEVEL_INFO = 3;
var ORYX_LOGLEVEL_WARN = 2;
var ORYX_LOGLEVEL_ERROR = 1;
var ORYX_LOGLEVEL_FATAL = 0;
var ORYX_LOGLEVEL = 3;
var ORYX_CONFIGURATION_DELAY = 100;
var ORYX_CONFIGURATION_WAIT_ATTEMPTS = 10;

if (!ORYX) var ORYX = {};

ORYX = Object.extend(ORYX, {
  //set the path in the config.js file!!!!
  PATH: ORYX.CONFIG.ROOT_PATH,
  //CONFIGURATION: "config.js",

  URLS: [],

  alreadyLoaded: [],

  configrationRetries: 0,

  Version: '0.1.1',

  availablePlugins: [],

  /**
   * The ORYX.Log logger.
   */
  Log: {
    __appenders: [
      {
        append: function (message) {
          if (typeof console !== 'undefined' && console.log !== undefined) {
            console.log(message);
          }
        },
      },
    ],

    trace: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_TRACE) ORYX.Log.__log('TRACE', arguments);
    },
    debug: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_DEBUG) ORYX.Log.__log('DEBUG', arguments);
    },
    info: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_INFO) ORYX.Log.__log('INFO', arguments);
    },
    warn: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_WARN) ORYX.Log.__log('WARN', arguments);
    },
    error: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_ERROR) ORYX.Log.__log('ERROR', arguments);
    },
    fatal: function () {
      if (ORYX_LOGLEVEL >= ORYX_LOGLEVEL_FATAL) ORYX.Log.__log('FATAL', arguments);
    },

    __log: function (prefix, messageParts) {
      messageParts[0] = new Date().getTime() + ' ' + prefix + ' ' + messageParts[0];
      var message = printf.apply(null, messageParts);

      ORYX.Log.__appenders.each(function (appender) {
        appender.append(message);
      });
    },

    addAppender: function (appender) {
      ORYX.Log.__appenders.push(appender);
    },
  },

  /**
   * First bootstrapping layer. The Oryx loading procedure begins. In this
   * step, all preliminaries that are not in the responsibility of Oryx to be
   * met have to be checked here, such as the existance of the prototpe
   * library in the current execution environment. After that, the second
   * bootstrapping layer is being invoked. Failing to ensure that any
   * preliminary condition is not met has to fail with an error.
   */
  load: function () {
    ORYX.Log.debug('Oryx begins loading procedure.');

    // check for prototype
    if (
      typeof Prototype == 'undefined' ||
      typeof Element == 'undefined' ||
      typeof Element.Methods == 'undefined' ||
      parseFloat(Prototype.Version.split('.')[0] + '.' + Prototype.Version.split('.')[1]) < 1.5
    )
      throw 'Application requires the Prototype JavaScript framework >= 1.5.3';

    ORYX.Log.debug('Prototype > 1.5 found.');

    // continue loading.
    ORYX._load();
  },

  /**
   * Second bootstrapping layer. The oryx configuration is checked. When not
   * yet loaded, config.js is being requested from the server. A repeated
   * error in retrieving the configuration will result in an error to be
   * thrown after a certain time of retries. Once the configuration is there,
   * all urls that are registered with oryx loading are being requested from
   * the server. Once everything is loaded, the third layer is being invoked.
   */
  _load: function () {
    /*
		// if configuration not there already,
		if(!(ORYX.CONFIG)) {
			
			// if this is the first attempt...
			if(ORYX.configrationRetries == 0) {
				
				// get the path and filename.
				var configuration = ORYX.PATH + ORYX.CONFIGURATION;
	
				ORYX.Log.debug("Configuration not found, loading from '%0'.",
					configuration);
				
				// require configuration file.
				Kickstart.require(configuration);
				
			// else if attempts exceeded ...
			} else if(ORYX.configrationRetries >= ORYX_CONFIGURATION_WAIT_ATTEMPTS) {
				
				throw "Tried to get configuration" +
					ORYX_CONFIGURATION_WAIT_ATTEMPTS +
					" times from '" + configuration + "'. Giving up."
					
			} else if(ORYX.configrationRetries > 0){
				
				// point out how many attempts are left...
				ORYX.Log.debug("Waiting once more (%0 attempts left)",
					(ORYX_CONFIGURATION_WAIT_ATTEMPTS -
						ORYX.configrationRetries));

			}
			
			// any case: continue in a moment with increased retry count.
			ORYX.configrationRetries++;
			window.setTimeout(ORYX._load, ORYX_CONFIGURATION_DELAY);
			return;
		}
		
		ORYX.Log.info("Configuration loaded.");
		
		// load necessary scripts.
		ORYX.URLS.each(function(url) {
			ORYX.Log.debug("Requireing '%0'", url);
			Kickstart.require(ORYX.PATH + url) });
	*/
    // configurate logging and load plugins.
    ORYX.loadPlugins();
  },

  /**
   * Third bootstrapping layer. This is where first the plugin coniguration
   * file is loaded into oryx, analyzed, and where all plugins are being
   * requested by the server. Afterwards, all editor instances will be
   * initialized.
   */
  loadPlugins: function () {
    // load plugins if enabled.
    if (ORYX.CONFIG.PLUGINS_ENABLED) ORYX._loadPlugins();
    else ORYX.Log.warn('Ignoring plugins, loading Core only.');

    // init the editor instances.
    init();
  },

  _loadPlugins: function () {
    // load plugin configuration file.
    var source = ORYX.CONFIG.PLUGINS_CONFIG;

    ORYX.Log.debug("Loading plugin configuration from '%0'.", source);

    new Ajax.Request(source, {
      asynchronous: false,
      method: 'get',
      onSuccess: function (result) {
        /*
         * This is the method that is being called when the plugin
         * configuration was successfully loaded from the server. The
         * file has to be processed and the contents need to be
         * considered for further plugin requireation.
         */

        ORYX.Log.info('Plugin configuration file loaded.');

        // get plugins.xml content
        var resultXml = result.responseXML;

        // TODO: Describe how properties are handled.
        // Get the globale Properties
        var globalProperties = [];
        var preferences = $A(resultXml.getElementsByTagName('properties'));
        preferences.each(function (p) {
          var props = $A(p.childNodes);
          props.each(function (prop) {
            var property = new Hash();

            // get all attributes from the node and set to global properties
            var attributes = $A(prop.attributes);
            attributes.each(function (attr) {
              property[attr.nodeName] = attr.nodeValue;
            });
            if (attributes.length > 0) {
              globalProperties.push(property);
            }
          });
        });

        // TODO Why are we using XML if we don't respect structure anyway?
        // for each plugin element in the configuration..
        var plugin = resultXml.getElementsByTagName('plugin');
        $A(plugin).each(function (node) {
          // get all element's attributes.
          // TODO: What about: var pluginData = $H(node.attributes) !?
          var pluginData = new Hash();
          $A(node.attributes).each(function (attr) {
            pluginData[attr.nodeName] = attr.nodeValue;
          });

          // ensure there's a name attribute.
          if (!pluginData['name']) {
            ORYX.Log.error('A plugin is not providing a name. Ingnoring this plugin.');
            return;
          }

          // ensure there's a source attribute.
          if (!pluginData['source']) {
            ORYX.Log.error("Plugin with name '%0' doesn't provide a source attribute.", pluginData['name']);
            return;
          }

          // Get all private Properties
          var propertyNodes = node.getElementsByTagName('property');
          var properties = [];
          $A(propertyNodes).each(function (prop) {
            var property = new Hash();

            // Get all Attributes from the Node
            var attributes = $A(prop.attributes);
            attributes.each(function (attr) {
              property[attr.nodeName] = attr.nodeValue;
            });
            if (attributes.length > 0) {
              properties.push(property);
            }
          });

          // Set all Global-Properties to the Properties
          properties = properties.concat(globalProperties);

          // Set Properties to Plugin-Data
          pluginData['properties'] = properties;

          // Get the RequieredNodes
          var requireNodes = node.getElementsByTagName('requires');
          var requires;
          $A(requireNodes).each(function (req) {
            var namespace = $A(req.attributes).find(function (attr) {
              return attr.name == 'namespace';
            });
            if (namespace && namespace.nodeValue) {
              if (!requires) {
                requires = { namespaces: [] };
              }

              requires.namespaces.push(namespace.nodeValue);
            }
          });

          // Set Requires to the Plugin-Data, if there is one
          if (requires) {
            pluginData['requires'] = requires;
          }

          // Get the RequieredNodes
          var notUsesInNodes = node.getElementsByTagName('notUsesIn');
          var notUsesIn;
          $A(notUsesInNodes).each(function (not) {
            var namespace = $A(not.attributes).find(function (attr) {
              return attr.name == 'namespace';
            });
            if (namespace && namespace.nodeValue) {
              if (!notUsesIn) {
                notUsesIn = { namespaces: [] };
              }

              notUsesIn.namespaces.push(namespace.nodeValue);
            }
          });

          // Set Requires to the Plugin-Data, if there is one
          if (notUsesIn) {
            pluginData['notUsesIn'] = notUsesIn;
          }

          var url = ORYX.PATH + ORYX.CONFIG.PLUGINS_FOLDER + pluginData['source'];

          ORYX.Log.debug("Requireing '%0'", url);

          // Add the Script-Tag to the Site
          //Kickstart.require(url);

          ORYX.Log.info("Plugin '%0' successfully loaded.", pluginData['name']);

          // Add the Plugin-Data to all available Plugins
          ORYX.availablePlugins.push(pluginData);
        });
      },
      onFailure: this._loadPluginsOnFails,
    });
  },

  _loadPluginsOnFails: function (result) {
    ORYX.Log.error('Plugin configuration file not available.');
  },
});

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

/**
 * EditPathHandler
 *
 * Edit SVG paths' coordinates according to specified from-to movement and
 * horizontal and vertical scaling factors.
 * The resulting path's d attribute is stored in instance variable d.
 *
 * @constructor
 */
ORYX.Core.SVG.EditPathHandler = Clazz.extend({
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    this.x = 0;
    this.y = 0;
    this.oldX = 0;
    this.oldY = 0;
    this.deltaWidth = 1;
    this.deltaHeight = 1;

    this.d = '';
  },

  /**
   * init
   *
   * @param {float} x Target point's x-coordinate
   * @param {float} y Target point's y-coordinate
   * @param {float} oldX Reference point's x-coordinate
   * @param {float} oldY Reference point's y-coordinate
   * @param {float} deltaWidth Horizontal scaling factor
   * @param {float} deltaHeight Vertical scaling factor
   */
  init: function (x, y, oldX, oldY, deltaWidth, deltaHeight) {
    this.x = x;
    this.y = y;
    this.oldX = oldX;
    this.oldY = oldY;
    this.deltaWidth = deltaWidth;
    this.deltaHeight = deltaHeight;

    this.d = '';
  },

  /**
   * editPointsAbs
   *
   * @param {Array} points Array of absolutePoints
   */
  editPointsAbs: function (points) {
    if (points instanceof Array) {
      var newPoints = [];
      var x, y;
      for (var i = 0; i < points.length; i++) {
        x = (parseFloat(points[i]) - this.oldX) * this.deltaWidth + this.x;
        i++;
        y = (parseFloat(points[i]) - this.oldY) * this.deltaHeight + this.y;
        newPoints.push(x);
        newPoints.push(y);
      }

      return newPoints;
    } else {
      //TODO error
    }
  },

  /**
   * editPointsRel
   *
   * @param {Array} points Array of absolutePoints
   */
  editPointsRel: function (points) {
    if (points instanceof Array) {
      var newPoints = [];
      var x, y;
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i]) * this.deltaWidth;
        i++;
        y = parseFloat(points[i]) * this.deltaHeight;
        newPoints.push(x);
        newPoints.push(y);
      }

      return newPoints;
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    var pointsAbs = this.editPointsAbs([x, y]);
    var pointsRel = this.editPointsRel([rx, ry]);

    this.d = this.d.concat(
      ' A' +
        pointsRel[0] +
        ' ' +
        pointsRel[1] +
        ' ' +
        xAxisRotation +
        ' ' +
        largeArcFlag +
        ' ' +
        sweepFlag +
        ' ' +
        pointsAbs[0] +
        ' ' +
        pointsAbs[1] +
        ' '
    );
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    var pointsRel = this.editPointsRel([rx, ry, x, y]);

    this.d = this.d.concat(
      ' a' +
        pointsRel[0] +
        ' ' +
        pointsRel[1] +
        ' ' +
        xAxisRotation +
        ' ' +
        largeArcFlag +
        ' ' +
        sweepFlag +
        ' ' +
        pointsRel[2] +
        ' ' +
        pointsRel[3] +
        ' '
    );
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function (x1, y1, x2, y2, x, y) {
    var pointsAbs = this.editPointsAbs([x1, y1, x2, y2, x, y]);

    this.d = this.d.concat(
      ' C' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ' + pointsAbs[2] + ' ' + pointsAbs[3] + ' ' + pointsAbs[4] + ' ' + pointsAbs[5] + ' '
    );
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function (x1, y1, x2, y2, x, y) {
    var pointsRel = this.editPointsRel([x1, y1, x2, y2, x, y]);

    this.d = this.d.concat(
      ' c' + pointsRel[0] + ' ' + pointsRel[1] + ' ' + pointsRel[2] + ' ' + pointsRel[3] + ' ' + pointsRel[4] + ' ' + pointsRel[5] + ' '
    );
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function (x) {
    var pointsAbs = this.editPointsAbs([x, 0]);

    this.d = this.d.concat(' H' + pointsAbs[0] + ' ');
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function (x) {
    var pointsRel = this.editPointsRel([x, 0]);

    this.d = this.d.concat(' h' + pointsRel[0] + ' ');
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function (x, y) {
    var pointsAbs = this.editPointsAbs([x, y]);

    this.d = this.d.concat(' L' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ');
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function (x, y) {
    var pointsRel = this.editPointsRel([x, y]);

    this.d = this.d.concat(' l' + pointsRel[0] + ' ' + pointsRel[1] + ' ');
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function (x, y) {
    var pointsAbs = this.editPointsAbs([x, y]);

    this.d = this.d.concat(' M' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ');
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function (x, y) {
    var pointsRel;
    if (this.d === '') {
      pointsRel = this.editPointsAbs([x, y]);
    } else {
      pointsRel = this.editPointsRel([x, y]);
    }

    this.d = this.d.concat(' m' + pointsRel[0] + ' ' + pointsRel[1] + ' ');
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function (x1, y1, x, y) {
    var pointsAbs = this.editPointsAbs([x1, y1, x, y]);

    this.d = this.d.concat(' Q' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ' + pointsAbs[2] + ' ' + pointsAbs[3] + ' ');
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function (x1, y1, x, y) {
    var pointsRel = this.editPointsRel([x1, y1, x, y]);

    this.d = this.d.concat(' q' + pointsRel[0] + ' ' + pointsRel[1] + ' ' + pointsRel[2] + ' ' + pointsRel[3] + ' ');
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function (x2, y2, x, y) {
    var pointsAbs = this.editPointsAbs([x2, y2, x, y]);

    this.d = this.d.concat(' S' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ' + pointsAbs[2] + ' ' + pointsAbs[3] + ' ');
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function (x2, y2, x, y) {
    var pointsRel = this.editPointsRel([x2, y2, x, y]);

    this.d = this.d.concat(' s' + pointsRel[0] + ' ' + pointsRel[1] + ' ' + pointsRel[2] + ' ' + pointsRel[3] + ' ');
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function (x, y) {
    var pointsAbs = this.editPointsAbs([x, y]);

    this.d = this.d.concat(' T' + pointsAbs[0] + ' ' + pointsAbs[1] + ' ');
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function (x, y) {
    var pointsRel = this.editPointsRel([x, y]);

    this.d = this.d.concat(' t' + pointsRel[0] + ' ' + pointsRel[1] + ' ');
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function (y) {
    var pointsAbs = this.editPointsAbs([0, y]);

    this.d = this.d.concat(' V' + pointsAbs[1] + ' ');
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function (y) {
    var pointsRel = this.editPointsRel([0, y]);

    this.d = this.d.concat(' v' + pointsRel[1] + ' ');
  },

  /**
   * closePath - z or Z
   */
  closePath: function () {
    this.d = this.d.concat(' z');
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

/**
 * MinMaxPathHandler
 *
 * Determine the minimum and maximum of a SVG path's absolute coordinates.
 * For relative coordinates the absolute value is computed for consideration.
 * The values are stored in attributes minX, minY, maxX, and maxY.
 *
 * @constructor
 */
ORYX.Core.SVG.MinMaxPathHandler = Clazz.extend({
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    this.minX = undefined;
    this.minY = undefined;
    this.maxX = undefined;
    this.maxY = undefined;

    this._lastAbsX = undefined;
    this._lastAbsY = undefined;
  },

  /**
   * Store minimal and maximal coordinates of passed points to attributes minX, maxX, minY, maxY
   *
   * @param {Array} points Array of absolutePoints
   */
  calculateMinMax: function (points) {
    if (points instanceof Array) {
      var x, y;
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i]);
        i++;
        y = parseFloat(points[i]);

        this.minX = this.minX !== undefined ? Math.min(this.minX, x) : x;
        this.maxX = this.maxX !== undefined ? Math.max(this.maxX, x) : x;
        this.minY = this.minY !== undefined ? Math.min(this.minY, y) : y;
        this.maxY = this.maxY !== undefined ? Math.max(this.maxY, y) : y;

        this._lastAbsX = x;
        this._lastAbsY = y;
      }
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.calculateMinMax([x, y]);
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function (x1, y1, x2, y2, x, y) {
    this.calculateMinMax([x1, y1, x2, y2, x, y]);
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function (x1, y1, x2, y2, x, y) {
    this.calculateMinMax([
      this._lastAbsX + x1,
      this._lastAbsY + y1,
      this._lastAbsX + x2,
      this._lastAbsY + y2,
      this._lastAbsX + x,
      this._lastAbsY + y,
    ]);
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function (x) {
    this.calculateMinMax([x, this._lastAbsY]);
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function (x) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY]);
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function (x, y) {
    this.calculateMinMax([x, y]);
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function (x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function (x, y) {
    this.calculateMinMax([x, y]);
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function (x, y) {
    if (this._lastAbsX && this._lastAbsY) {
      this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]);
    } else {
      this.calculateMinMax([x, y]);
    }
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function (x1, y1, x, y) {
    this.calculateMinMax([x1, y1, x, y]);
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function (x1, y1, x, y) {
    this.calculateMinMax([this._lastAbsX + x1, this._lastAbsY + y1, this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function (x2, y2, x, y) {
    this.calculateMinMax([x2, y2, x, y]);
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function (x2, y2, x, y) {
    this.calculateMinMax([this._lastAbsX + x2, this._lastAbsY + y2, this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function (x, y) {
    this.calculateMinMax([x, y]);
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function (x, y) {
    this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function (y) {
    this.calculateMinMax([this._lastAbsX, y]);
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function (y) {
    this.calculateMinMax([this._lastAbsX, this._lastAbsY + y]);
  },

  /**
   * closePath - z or Z
   */
  closePath: function () {
    return; // do nothing
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

/**
 * PathHandler
 *
 * Determine absolute points of a SVG path. The coordinates are stored
 * sequentially in the attribute points (x-coordinates at even indices,
 * y-coordinates at odd indices).
 *
 * @constructor
 */
ORYX.Core.SVG.PointsPathHandler = Clazz.extend({
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    this.points = [];

    this._lastAbsX = undefined;
    this._lastAbsY = undefined;
  },

  /**
   * addPoints
   *
   * @param {Array} points Array of absolutePoints
   */
  addPoints: function (points) {
    if (points instanceof Array) {
      var x, y;
      for (var i = 0; i < points.length; i++) {
        x = parseFloat(points[i]);
        i++;
        y = parseFloat(points[i]);

        this.points.push(x);
        this.points.push(y);
        //this.points.push({x:x, y:y});

        this._lastAbsX = x;
        this._lastAbsY = y;
      }
    } else {
      //TODO error
    }
  },

  /**
   * arcAbs - A
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcAbs: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.addPoints([x, y]);
  },

  /**
   * arcRel - a
   *
   * @param {Number} rx
   * @param {Number} ry
   * @param {Number} xAxisRotation
   * @param {Boolean} largeArcFlag
   * @param {Boolean} sweepFlag
   * @param {Number} x
   * @param {Number} y
   */
  arcRel: function (rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoCubicAbs - C
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicAbs: function (x1, y1, x2, y2, x, y) {
    this.addPoints([x, y]);
  },

  /**
   * curvetoCubicRel - c
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicRel: function (x1, y1, x2, y2, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * linetoHorizontalAbs - H
   *
   * @param {Number} x
   */
  linetoHorizontalAbs: function (x) {
    this.addPoints([x, this._lastAbsY]);
  },

  /**
   * linetoHorizontalRel - h
   *
   * @param {Number} x
   */
  linetoHorizontalRel: function (x) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY]);
  },

  /**
   * linetoAbs - L
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoAbs: function (x, y) {
    this.addPoints([x, y]);
  },

  /**
   * linetoRel - l
   *
   * @param {Number} x
   * @param {Number} y
   */
  linetoRel: function (x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * movetoAbs - M
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoAbs: function (x, y) {
    this.addPoints([x, y]);
  },

  /**
   * movetoRel - m
   *
   * @param {Number} x
   * @param {Number} y
   */
  movetoRel: function (x, y) {
    if (this._lastAbsX && this._lastAbsY) {
      this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
    } else {
      this.addPoints([x, y]);
    }
  },

  /**
   * curvetoQuadraticAbs - Q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticAbs: function (x1, y1, x, y) {
    this.addPoints([x, y]);
  },

  /**
   * curvetoQuadraticRel - q
   *
   * @param {Number} x1
   * @param {Number} y1
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticRel: function (x1, y1, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoCubicSmoothAbs - S
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothAbs: function (x2, y2, x, y) {
    this.addPoints([x, y]);
  },

  /**
   * curvetoCubicSmoothRel - s
   *
   * @param {Number} x2
   * @param {Number} y2
   * @param {Number} x
   * @param {Number} y
   */
  curvetoCubicSmoothRel: function (x2, y2, x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * curvetoQuadraticSmoothAbs - T
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothAbs: function (x, y) {
    this.addPoints([x, y]);
  },

  /**
   * curvetoQuadraticSmoothRel - t
   *
   * @param {Number} x
   * @param {Number} y
   */
  curvetoQuadraticSmoothRel: function (x, y) {
    this.addPoints([this._lastAbsX + x, this._lastAbsY + y]);
  },

  /**
   * linetoVerticalAbs - V
   *
   * @param {Number} y
   */
  linetoVerticalAbs: function (y) {
    this.addPoints([this._lastAbsX, y]);
  },

  /**
   * linetoVerticalRel - v
   *
   * @param {Number} y
   */
  linetoVerticalRel: function (y) {
    this.addPoints([this._lastAbsX, this._lastAbsY + y]);
  },

  /**
   * closePath - z or Z
   */
  closePath: function () {
    return; // do nothing
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 *
 * Config variables
 */
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx';
NAMESPACE_SVG = 'http://www.w3.org/2000/svg/';

/**
 * @classDescription This class wraps the manipulation of a SVG marker.
 * @namespace ORYX.Core.SVG
 * uses Inheritance (Clazz)
 * uses Prototype 1.5.0
 *
 */

/**
 * Init package
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

ORYX.Core.SVG.SVGMarker = Clazz.extend({
  /**
   * Constructor
   * @param markerElement {SVGMarkerElement}
   */
  construct: function (markerElement) {
    arguments.callee.$.construct.apply(this, arguments);

    this.id = undefined;
    this.element = markerElement;
    this.refX = undefined;
    this.refY = undefined;
    this.markerWidth = undefined;
    this.markerHeight = undefined;
    this.oldRefX = undefined;
    this.oldRefY = undefined;
    this.oldMarkerWidth = undefined;
    this.oldMarkerHeight = undefined;
    this.optional = false;
    this.enabled = true;
    this.minimumLength = undefined;
    this.resize = false;

    this.svgShapes = [];

    this._init(); //initialisation of all the properties declared above.
  },

  /**
   * Initializes the values that are defined in the constructor.
   */
  _init: function () {
    //check if this.element is a SVGMarkerElement
    if (!(this.element == '[object SVGMarkerElement]')) {
      throw 'SVGMarker: Argument is not an instance of SVGMarkerElement.';
    }

    this.id = this.element.getAttributeNS(null, 'id');

    //init svg marker attributes
    var refXValue = this.element.getAttributeNS(null, 'refX');
    if (refXValue) {
      this.refX = parseFloat(refXValue);
    } else {
      this.refX = 0;
    }
    var refYValue = this.element.getAttributeNS(null, 'refY');
    if (refYValue) {
      this.refY = parseFloat(refYValue);
    } else {
      this.refY = 0;
    }
    var markerWidthValue = this.element.getAttributeNS(null, 'markerWidth');
    if (markerWidthValue) {
      this.markerWidth = parseFloat(markerWidthValue);
    } else {
      this.markerWidth = 3;
    }
    var markerHeightValue = this.element.getAttributeNS(null, 'markerHeight');
    if (markerHeightValue) {
      this.markerHeight = parseFloat(markerHeightValue);
    } else {
      this.markerHeight = 3;
    }

    this.oldRefX = this.refX;
    this.oldRefY = this.refY;
    this.oldMarkerWidth = this.markerWidth;
    this.oldMarkerHeight = this.markerHeight;

    //init oryx attributes
    var optionalAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'optional');
    if (optionalAttr) {
      optionalAttr = optionalAttr.strip();
      this.optional = optionalAttr.toLowerCase() === 'yes';
    } else {
      this.optional = false;
    }

    var enabledAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'enabled');
    if (enabledAttr) {
      enabledAttr = enabledAttr.strip();
      this.enabled = !(enabledAttr.toLowerCase() === 'no');
    } else {
      this.enabled = true;
    }

    var minLengthAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'minimumLength');
    if (minLengthAttr) {
      this.minimumLength = parseFloat(minLengthAttr);
    }

    var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resize');
    if (resizeAttr) {
      resizeAttr = resizeAttr.strip();
      this.resize = resizeAttr.toLowerCase() === 'yes';
    } else {
      this.resize = false;
    }

    //init SVGShape objects
    //this.svgShapes = this._getSVGShapes(this.element);
  },

  /**
   *
   */
  _getSVGShapes: function (svgElement) {
    if (svgElement.hasChildNodes) {
      var svgShapes = [];
      var me = this;
      $A(svgElement.childNodes).each(function (svgChild) {
        try {
          var svgShape = new ORYX.Core.SVG.SVGShape(svgChild);
          svgShapes.push(svgShape);
        } catch (e) {
          svgShapes = svgShapes.concat(me._getSVGShapes(svgChild));
        }
      });
      return svgShapes;
    }
  },

  /**
   * Writes the changed values into the SVG marker.
   */
  update: function () {
    //TODO mache marker resizebar!!! aber erst wenn der rest der connectingshape funzt!

    //		//update marker attributes
    //		if(this.refX != this.oldRefX) {
    //			this.element.setAttributeNS(null, "refX", this.refX);
    //		}
    //		if(this.refY != this.oldRefY) {
    //			this.element.setAttributeNS(null, "refY", this.refY);
    //		}
    //		if(this.markerWidth != this.oldMarkerWidth) {
    //			this.element.setAttributeNS(null, "markerWidth", this.markerWidth);
    //		}
    //		if(this.markerHeight != this.oldMarkerHeight) {
    //			this.element.setAttributeNS(null, "markerHeight", this.markerHeight);
    //		}
    //
    //		//update SVGShape objects
    //		var widthDelta = this.markerWidth / this.oldMarkerWidth;
    //		var heightDelta = this.markerHeight / this.oldMarkerHeight;
    //		if(widthDelta != 1 && heightDelta != 1) {
    //			this.svgShapes.each(function(svgShape) {
    //
    //			});
    //		}

    //update old values to prepare the next update
    this.oldRefX = this.refX;
    this.oldRefY = this.refY;
    this.oldMarkerWidth = this.markerWidth;
    this.oldMarkerHeight = this.markerHeight;
  },

  toString: function () {
    return this.element ? 'SVGMarker ' + this.element.id : 'SVGMarker ' + this.element;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 *
 * Config variables
 */
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx';
NAMESPACE_SVG = 'http://www.w3.org/2000/svg/';

/**
 * @classDescription This class wraps the manipulation of a SVG basic shape or a path.
 * @namespace ORYX.Core.SVG
 * uses Inheritance (Clazz)
 * uses Prototype 1.5.0
 * uses PathParser by Kevin Lindsey (http://kevlindev.com/)
 * uses MinMaxPathHandler
 * uses EditPathHandler
 *
 */

//init package
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

ORYX.Core.SVG.SVGShape = Clazz.extend({
  /**
   * Constructor
   * @param svgElem {SVGElement} An SVGElement that is a basic shape or a path.
   */
  construct: function (svgElem) {
    arguments.callee.$.construct.apply(this, arguments);

    this.type;
    this.element = svgElem;
    this.x = undefined;
    this.y = undefined;
    this.width = undefined;
    this.height = undefined;
    this.oldX = undefined;
    this.oldY = undefined;
    this.oldWidth = undefined;
    this.oldHeight = undefined;
    this.radiusX = undefined;
    this.radiusY = undefined;
    this.isHorizontallyResizable = false;
    this.isVerticallyResizable = false;
    //this.anchors = [];
    this.anchorLeft = false;
    this.anchorRight = false;
    this.anchorTop = false;
    this.anchorBottom = false;

    //attributes of path elements of edge objects
    this.allowDockers = true;
    this.resizeMarkerMid = false;

    this.editPathParser;
    this.editPathHandler;

    this.init(); //initialisation of all the properties declared above.
  },

  /**
   * Initializes the values that are defined in the constructor.
   */
  init: function () {
    /**initialize position and size*/
    if (ORYX.Editor.checkClassType(this.element, SVGRectElement) || ORYX.Editor.checkClassType(this.element, SVGImageElement)) {
      this.type = 'Rect';

      var xAttr = this.element.getAttributeNS(null, 'x');
      if (xAttr) {
        this.oldX = parseFloat(xAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var yAttr = this.element.getAttributeNS(null, 'y');
      if (yAttr) {
        this.oldY = parseFloat(yAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var widthAttr = this.element.getAttributeNS(null, 'width');
      if (widthAttr) {
        this.oldWidth = parseFloat(widthAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var heightAttr = this.element.getAttributeNS(null, 'height');
      if (heightAttr) {
        this.oldHeight = parseFloat(heightAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
    } else if (ORYX.Editor.checkClassType(this.element, SVGCircleElement)) {
      this.type = 'Circle';

      var cx = undefined;
      var cy = undefined;
      //var r = undefined;

      var cxAttr = this.element.getAttributeNS(null, 'cx');
      if (cxAttr) {
        cx = parseFloat(cxAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var cyAttr = this.element.getAttributeNS(null, 'cy');
      if (cyAttr) {
        cy = parseFloat(cyAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var rAttr = this.element.getAttributeNS(null, 'r');
      if (rAttr) {
        //r = parseFloat(rAttr);
        this.radiusX = parseFloat(rAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      this.oldX = cx - this.radiusX;
      this.oldY = cy - this.radiusX;
      this.oldWidth = 2 * this.radiusX;
      this.oldHeight = 2 * this.radiusX;
    } else if (ORYX.Editor.checkClassType(this.element, SVGEllipseElement)) {
      this.type = 'Ellipse';

      var cx = undefined;
      var cy = undefined;
      //var rx = undefined;
      //var ry = undefined;
      var cxAttr = this.element.getAttributeNS(null, 'cx');
      if (cxAttr) {
        cx = parseFloat(cxAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var cyAttr = this.element.getAttributeNS(null, 'cy');
      if (cyAttr) {
        cy = parseFloat(cyAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var rxAttr = this.element.getAttributeNS(null, 'rx');
      if (rxAttr) {
        this.radiusX = parseFloat(rxAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var ryAttr = this.element.getAttributeNS(null, 'ry');
      if (ryAttr) {
        this.radiusY = parseFloat(ryAttr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      this.oldX = cx - this.radiusX;
      this.oldY = cy - this.radiusY;
      this.oldWidth = 2 * this.radiusX;
      this.oldHeight = 2 * this.radiusY;
    } else if (ORYX.Editor.checkClassType(this.element, SVGLineElement)) {
      this.type = 'Line';

      var x1 = undefined;
      var y1 = undefined;
      var x2 = undefined;
      var y2 = undefined;
      var x1Attr = this.element.getAttributeNS(null, 'x1');
      if (x1Attr) {
        x1 = parseFloat(x1Attr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var y1Attr = this.element.getAttributeNS(null, 'y1');
      if (y1Attr) {
        y1 = parseFloat(y1Attr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var x2Attr = this.element.getAttributeNS(null, 'x2');
      if (x2Attr) {
        x2 = parseFloat(x2Attr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      var y2Attr = this.element.getAttributeNS(null, 'y2');
      if (y2Attr) {
        y2 = parseFloat(y2Attr);
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
      this.oldX = Math.min(x1, x2);
      this.oldY = Math.min(y1, y2);
      this.oldWidth = Math.abs(x1 - x2);
      this.oldHeight = Math.abs(y1 - y2);
    } else if (
      ORYX.Editor.checkClassType(this.element, SVGPolylineElement) ||
      ORYX.Editor.checkClassType(this.element, SVGPolygonElement)
    ) {
      this.type = 'Polyline';

      var pointsArray = [];
      if (this.element.points && this.element.points.numberOfItems) {
        for (var i = 0, size = this.element.points.numberOfItems; i < size; i++) {
          pointsArray.push(this.element.points.getItem(i).x);
          pointsArray.push(this.element.points.getItem(i).y);
        }
      } else {
        var points = this.element.getAttributeNS(null, 'points');
        if (points) {
          points = points.replace(/,/g, ' ');
          pointsArray = points.split(' ');
          pointsArray = pointsArray.without('');
        } else {
          throw 'Missing attribute in element ' + this.element;
        }
      }

      if (pointsArray && pointsArray.length && pointsArray.length > 1) {
        var minX = parseFloat(pointsArray[0]);
        var minY = parseFloat(pointsArray[1]);
        var maxX = parseFloat(pointsArray[0]);
        var maxY = parseFloat(pointsArray[1]);

        for (var i = 0; i < pointsArray.length; i++) {
          minX = Math.min(minX, parseFloat(pointsArray[i]));
          maxX = Math.max(maxX, parseFloat(pointsArray[i]));
          i++;
          minY = Math.min(minY, parseFloat(pointsArray[i]));
          maxY = Math.max(maxY, parseFloat(pointsArray[i]));
        }

        this.oldX = minX;
        this.oldY = minY;
        this.oldWidth = maxX - minX;
        this.oldHeight = maxY - minY;
      } else {
        throw 'Missing attribute in element ' + this.element;
      }
    } else if (ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
      this.type = 'Path';

      this.editPathParser = new PathParser();
      this.editPathHandler = new ORYX.Core.SVG.EditPathHandler();
      this.editPathParser.setHandler(this.editPathHandler);

      var parser = new PathParser();
      var handler = new ORYX.Core.SVG.MinMaxPathHandler();
      parser.setHandler(handler);
      parser.parsePath(this.element);

      this.oldX = handler.minX;
      this.oldY = handler.minY;
      this.oldWidth = handler.maxX - handler.minX;
      this.oldHeight = handler.maxY - handler.minY;

      delete parser;
      delete handler;
    } else {
      throw 'Element is not a shape.';
    }

    /** initialize attributes of oryx namespace */
    //resize
    var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resize');
    if (resizeAttr) {
      resizeAttr = resizeAttr.toLowerCase();
      if (resizeAttr.match(/horizontal/)) {
        this.isHorizontallyResizable = true;
      } else {
        this.isHorizontallyResizable = false;
      }
      if (resizeAttr.match(/vertical/)) {
        this.isVerticallyResizable = true;
      } else {
        this.isVerticallyResizable = false;
      }
    } else {
      this.isHorizontallyResizable = false;
      this.isVerticallyResizable = false;
    }

    //anchors
    var anchorAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'anchors');
    if (anchorAttr) {
      anchorAttr = anchorAttr.replace('/,/g', ' ');
      var anchors = anchorAttr.split(' ').without('');

      for (var i = 0; i < anchors.length; i++) {
        switch (anchors[i].toLowerCase()) {
          case 'left':
            this.anchorLeft = true;
            break;
          case 'right':
            this.anchorRight = true;
            break;
          case 'top':
            this.anchorTop = true;
            break;
          case 'bottom':
            this.anchorBottom = true;
            break;
        }
      }
    }

    //allowDockers and resizeMarkerMid
    if (ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
      var allowDockersAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'allowDockers');
      if (allowDockersAttr) {
        if (allowDockersAttr.toLowerCase() === 'no') {
          this.allowDockers = false;
        } else {
          this.allowDockers = true;
        }
      }

      var resizeMarkerMidAttr = this.element.getAttributeNS(NAMESPACE_ORYX, 'resizeMarker-mid');
      if (resizeMarkerMidAttr) {
        if (resizeMarkerMidAttr.toLowerCase() === 'yes') {
          this.resizeMarkerMid = true;
        } else {
          this.resizeMarkerMid = false;
        }
      }
    }

    this.x = this.oldX;
    this.y = this.oldY;
    this.width = this.oldWidth;
    this.height = this.oldHeight;
  },

  /**
   * Writes the changed values into the SVG element.
   */
  update: function () {
    if (this.x !== this.oldX || this.y !== this.oldY || this.width !== this.oldWidth || this.height !== this.oldHeight) {
      switch (this.type) {
        case 'Rect':
          if (this.x !== this.oldX) this.element.setAttributeNS(null, 'x', this.x);
          if (this.y !== this.oldY) this.element.setAttributeNS(null, 'y', this.y);
          if (this.width !== this.oldWidth) this.element.setAttributeNS(null, 'width', this.width);
          if (this.height !== this.oldHeight) this.element.setAttributeNS(null, 'height', this.height);
          break;
        case 'Circle':
          //calculate the radius
          //var r;
          //					if(this.width/this.oldWidth <= this.height/this.oldHeight) {
          //						this.radiusX = ((this.width > this.height) ? this.width : this.height)/2.0;
          //					} else {
          this.radiusX = (this.width < this.height ? this.width : this.height) / 2.0;
          //}

          this.element.setAttributeNS(null, 'cx', this.x + this.width / 2.0);
          this.element.setAttributeNS(null, 'cy', this.y + this.height / 2.0);
          this.element.setAttributeNS(null, 'r', this.radiusX);
          break;
        case 'Ellipse':
          this.radiusX = this.width / 2;
          this.radiusY = this.height / 2;

          this.element.setAttributeNS(null, 'cx', this.x + this.radiusX);
          this.element.setAttributeNS(null, 'cy', this.y + this.radiusY);
          this.element.setAttributeNS(null, 'rx', this.radiusX);
          this.element.setAttributeNS(null, 'ry', this.radiusY);
          break;
        case 'Line':
          if (this.x !== this.oldX) this.element.setAttributeNS(null, 'x1', this.x);

          if (this.y !== this.oldY) this.element.setAttributeNS(null, 'y1', this.y);

          if (this.x !== this.oldX || this.width !== this.oldWidth) this.element.setAttributeNS(null, 'x2', this.x + this.width);

          if (this.y !== this.oldY || this.height !== this.oldHeight) this.element.setAttributeNS(null, 'y2', this.y + this.height);
          break;
        case 'Polyline':
          var points = this.element.getAttributeNS(null, 'points');
          if (points) {
            points = points.replace(/,/g, ' ').split(' ').without('');

            if (points && points.length && points.length > 1) {
              //TODO what if oldWidth == 0?
              var widthDelta = this.oldWidth === 0 ? 0 : this.width / this.oldWidth;
              var heightDelta = this.oldHeight === 0 ? 0 : this.height / this.oldHeight;

              var updatedPoints = '';
              for (var i = 0; i < points.length; i++) {
                var x = (parseFloat(points[i]) - this.oldX) * widthDelta + this.x;
                i++;
                var y = (parseFloat(points[i]) - this.oldY) * heightDelta + this.y;
                updatedPoints += x + ' ' + y + ' ';
              }
              this.element.setAttributeNS(null, 'points', updatedPoints);
            } else {
              //TODO error
            }
          } else {
            //TODO error
          }
          break;
        case 'Path':
          //calculate scaling delta
          //TODO what if oldWidth == 0?
          var widthDelta = this.oldWidth === 0 ? 0 : this.width / this.oldWidth;
          var heightDelta = this.oldHeight === 0 ? 0 : this.height / this.oldHeight;

          //use path parser to edit each point of the path
          this.editPathHandler.init(this.x, this.y, this.oldX, this.oldY, widthDelta, heightDelta);
          this.editPathParser.parsePath(this.element);

          //change d attribute of path
          this.element.setAttributeNS(null, 'd', this.editPathHandler.d);
          break;
      }

      this.oldX = this.x;
      this.oldY = this.y;
      this.oldWidth = this.width;
      this.oldHeight = this.height;
    }

    // Remove cached variables
    delete this.visible;
    delete this.handler;
  },

  isPointIncluded: function (pointX, pointY) {
    // Check if there are the right arguments and if the node is visible
    if (!pointX || !pointY || !this.isVisible()) {
      return false;
    }

    switch (this.type) {
      case 'Rect':
        return pointX >= this.x && pointX <= this.x + this.width && pointY >= this.y && pointY <= this.y + this.height;
        break;
      case 'Circle':
        //calculate the radius
        //				var r;
        //				if(this.width/this.oldWidth <= this.height/this.oldHeight) {
        //					r = ((this.width > this.height) ? this.width : this.height)/2.0;
        //				} else {
        //				 	r = ((this.width < this.height) ? this.width : this.height)/2.0;
        //				}
        return ORYX.Core.Math.isPointInEllipse(
          pointX,
          pointY,
          this.x + this.width / 2.0,
          this.y + this.height / 2.0,
          this.radiusX,
          this.radiusX
        );
        break;
      case 'Ellipse':
        return ORYX.Core.Math.isPointInEllipse(pointX, pointY, this.x + this.radiusX, this.y + this.radiusY, this.radiusX, this.radiusY);
        break;
      case 'Line':
        return ORYX.Core.Math.isPointInLine(pointX, pointY, this.x, this.y, this.x + this.width, this.y + this.height);
        break;
      case 'Polyline':
        var points = this.element.getAttributeNS(null, 'points');

        if (points) {
          points = points.replace(/,/g, ' ').split(' ').without('');

          points = points.collect(function (n) {
            return parseFloat(n);
          });

          return ORYX.Core.Math.isPointInPolygone(pointX, pointY, points);
        } else {
          return false;
        }
        break;
      case 'Path':
        // Cache Path handler
        if (!this.handler) {
          var parser = new PathParser();
          this.handler = new ORYX.Core.SVG.PointsPathHandler();
          parser.setHandler(this.handler);
          parser.parsePath(this.element);
        }

        return ORYX.Core.Math.isPointInPolygone(pointX, pointY, this.handler.points);

        break;
      default:
        return false;
    }
  },

  /**
   * Returns true if the element is visible
   * @param {SVGElement} elem
   * @return boolean
   */
  isVisible: function (elem) {
    if (this.visible !== undefined) {
      return this.visible;
    }

    if (!elem) {
      elem = this.element;
    }

    var hasOwnerSVG = false;
    try {
      hasOwnerSVG = !!elem.ownerSVGElement;
    } catch (e) {}

    // Is SVG context
    if (hasOwnerSVG) {
      // IF G-Element
      if (ORYX.Editor.checkClassType(elem, SVGGElement)) {
        if (elem.className && elem.className.baseVal == 'me') {
          this.visible = true;
          return this.visible;
        }
      }

      // Check if fill or stroke is set
      var fill = elem.getAttributeNS(null, 'fill');
      var stroke = elem.getAttributeNS(null, 'stroke');
      if (fill && fill == 'none' && stroke && stroke == 'none') {
        this.visible = false;
      } else {
        // Check if displayed
        var attr = elem.getAttributeNS(null, 'display');
        if (!attr) this.visible = this.isVisible(elem.parentNode);
        else if (attr == 'none') this.visible = false;
        else this.visible = true;
      }
    } else {
      this.visible = true;
    }

    return this.visible;
  },

  toString: function () {
    return this.element ? 'SVGShape ' + this.element.id : 'SVGShape ' + this.element;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.SVG) {
  ORYX.Core.SVG = {};
}

/**
 * @classDescription Class for adding text to a shape.
 *
 */
ORYX.Core.SVG.Label = Clazz.extend({
  _characterSets: [
    '%W',
    '@',
    'm',
    'wDGMOQ?????#+=<>~^',
    'ABCHKNRSUVXZ??????????&',
    'bdghnopqux???????????ETY1234567890?????_????${}*????`???????????',
    'aeksvyz?????FLP????????????????',
    'c-',
    'rtJ"/()[]:;!|\\',
    'fjI., ',
    "'",
    'il',
  ],
  _characterSetValues: [15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3],

  /**
   * Constructor
   * @param options {Object} :
   * 	textElement
   *
   */
  construct: function (options) {
    arguments.callee.$.construct.apply(this, arguments);

    if (!options.textElement) {
      throw 'Label: No parameter textElement.';
    } else if (!ORYX.Editor.checkClassType(options.textElement, SVGTextElement)) {
      throw 'Label: Parameter textElement is not an SVGTextElement.';
    }

    this.invisibleRenderPoint = -5000;

    this.node = options.textElement;

    this.node.setAttributeNS(null, 'stroke-width', '0pt');
    this.node.setAttributeNS(null, 'letter-spacing', '-0.01px');

    this.shapeId = options.shapeId;

    this.id;

    this.fitToElemId;

    this.edgePosition;

    this.x;
    this.y;
    this.oldX;
    this.oldY;

    this.isVisible = true;

    this._text;
    this._verticalAlign;
    this._horizontalAlign;
    this._rotate;
    this._rotationPoint;

    //this.anchors = [];
    this.anchorLeft;
    this.anchorRight;
    this.anchorTop;
    this.anchorBottom;

    this._isChanged = true;

    //if the text element already has an id, don't change it.
    var _id = this.node.getAttributeNS(null, 'id');
    if (_id) {
      this.id = _id;
    }

    //initialization

    //set referenced element the text is fit to
    this.fitToElemId = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'fittoelem');
    if (this.fitToElemId) this.fitToElemId = this.shapeId + this.fitToElemId;

    //set alignment
    var alignValues = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align');
    if (alignValues) {
      alignValues = alignValues.replace(/,/g, ' ');
      alignValues = alignValues.split(' ');
      alignValues = alignValues.without('');

      alignValues.each(
        function (alignValue) {
          switch (alignValue) {
            case 'top':
            case 'middle':
            case 'bottom':
              if (!this._verticalAlign) {
                this._originVerticalAlign = this._verticalAlign = alignValue;
              }
              break;
            case 'left':
            case 'center':
            case 'right':
              if (!this._horizontalAlign) {
                this._originHorizontalAlign = this._horizontalAlign = alignValue;
              }
              break;
          }
        }.bind(this)
      );
    }

    //set edge position (only in case the label belongs to an edge)
    this.edgePosition = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'edgePosition');
    if (this.edgePosition) {
      this.originEdgePosition = this.edgePosition = this.edgePosition.toLowerCase();
    }

    //get offset top
    this.offsetTop = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetTop') || ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP;
    if (this.offsetTop) {
      this.offsetTop = parseInt(this.offsetTop);
    }

    //get offset top
    this.offsetBottom = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetBottom') || ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM;
    if (this.offsetBottom) {
      this.offsetBottom = parseInt(this.offsetBottom);
    }

    //set rotation
    var rotateValue = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'rotate');
    if (rotateValue) {
      try {
        this._rotate = parseFloat(rotateValue);
      } catch (e) {
        this._rotate = 0;
      }
    } else {
      this._rotate = 0;
    }

    //anchors
    var anchorAttr = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors');
    if (anchorAttr) {
      anchorAttr = anchorAttr.replace('/,/g', ' ');
      var anchors = anchorAttr.split(' ').without('');

      for (var i = 0; i < anchors.length; i++) {
        switch (anchors[i].toLowerCase()) {
          case 'left':
            this.originAnchorLeft = this.anchorLeft = true;
            break;
          case 'right':
            this.originAnchorRight = this.anchorRight = true;
            break;
          case 'top':
            this.originAnchorTop = this.anchorTop = true;
            break;
          case 'bottom':
            this.originAnchorBottom = this.anchorBottom = true;
            break;
        }
      }
    }

    //if no alignment defined, set default alignment
    if (!this._verticalAlign) {
      this._verticalAlign = 'bottom';
    }
    if (!this._horizontalAlign) {
      this._horizontalAlign = 'left';
    }

    var xValue = this.node.getAttributeNS(null, 'x');
    if (xValue) {
      this.oldX = this.x = parseFloat(xValue);
    } else {
      //TODO error
    }

    var yValue = this.node.getAttributeNS(null, 'y');
    if (yValue) {
      this.oldY = this.y = parseFloat(yValue);
    } else {
      //TODO error
    }

    //set initial text
    this.text(this.node.textContent);
  },

  /**
   * Reset the anchor position to the original value
   * which was specified in the stencil set
   *
   */
  resetAnchorPosition: function () {
    this.anchorLeft = this.originAnchorLeft || false;
    this.anchorRight = this.originAnchorRight || false;
    this.anchorTop = this.originAnchorTop || false;
    this.anchorBottom = this.originAnchorBottom || false;
  },

  isOriginAnchorLeft: function () {
    return this.originAnchorLeft || false;
  },
  isOriginAnchorRight: function () {
    return this.originAnchorRight || false;
  },
  isOriginAnchorTop: function () {
    return this.originAnchorTop || false;
  },
  isOriginAnchorBottom: function () {
    return this.originAnchorBottom || false;
  },

  isAnchorLeft: function () {
    return this.anchorLeft || false;
  },
  isAnchorRight: function () {
    return this.anchorRight || false;
  },
  isAnchorTop: function () {
    return this.anchorTop || false;
  },
  isAnchorBottom: function () {
    return this.anchorBottom || false;
  },

  /**
   * Returns the x coordinate
   * @return {number}
   */
  getX: function () {
    try {
      var x = this.node.x.baseVal.getItem(0).value;
      switch (this.horizontalAlign()) {
        case 'left':
          return x;
        case 'center':
          return x - this.getWidth() / 2;
        case 'right':
          return x - this.getWidth();
      }
      return this.node.getBBox().x;
    } catch (e) {
      return this.x;
    }
  },

  setX: function (x) {
    if (this.position) this.position.x = x;
    else this.setOriginX(x);
  },

  /**
   * Returns the y coordinate
   * @return {number}
   */
  getY: function () {
    try {
      return this.node.getBBox().y;
    } catch (e) {
      return this.y;
    }
  },

  setY: function (y) {
    if (this.position) this.position.y = y;
    else this.setOriginY(y);
  },

  setOriginX: function (x) {
    this.x = x;
  },

  setOriginY: function (y) {
    this.y = y;
  },

  /**
   * Returns the width of the label
   * @return {number}
   */
  getWidth: function () {
    try {
      try {
        var width,
          cn = this.node.childNodes;
        if (cn.length == 0) {
          width = this.node.getBBox().width;
        } else {
          for (var i = 0, size = cn.length; i < size; ++i) {
            var w = cn[i].getComputedTextLength();
            if ('undefined' == typeof width || width < w) {
              width = w;
            }
          }
        }
        return width + (width % 2 == 0 ? 0 : 1);
      } catch (ee) {
        return this.node.getBBox().width;
      }
    } catch (e) {
      return 0;
    }
  },

  getOriginUpperLeft: function () {
    var x = this.x,
      y = this.y;
    switch (this._horizontalAlign) {
      case 'center':
        x -= this.getWidth() / 2;
        break;
      case 'right':
        x -= this.getWidth();
        break;
    }
    switch (this._verticalAlign) {
      case 'middle':
        y -= this.getHeight() / 2;
        break;
      case 'bottom':
        y -= this.getHeight();
        break;
    }
    return { x: x, y: y };
  },

  /**
   * Returns the height of the label
   * @return {number}
   */
  getHeight: function () {
    try {
      return this.node.getBBox().height;
    } catch (e) {
      return 0;
    }
  },

  /**
   * Returns the relative center position of the label
   * to its parent shape.
   * @return {Object}
   */
  getCenter: function () {
    var up = { x: this.getX(), y: this.getY() };
    up.x += this.getWidth() / 2;
    up.y += this.getHeight() / 2;
    return up;
  },

  /**
   * Sets the position of a label relative to the parent.
   * @param {Object} position
   */
  setPosition: function (position) {
    if (!position || position.x === undefined || position.y === undefined) {
      delete this.position;
    } else {
      this.position = position;
    }

    if (this.position) {
      delete this._referencePoint;
      delete this.edgePosition;
    }

    this._isChanged = true;
    this.update();
  },

  /**
   * Return the position
   */
  getPosition: function () {
    return this.position;
  },

  setReferencePoint: function (ref) {
    if (ref) {
      this._referencePoint = ref;
    } else {
      delete this._referencePoint;
    }
    if (this._referencePoint) {
      delete this.position;
    }
  },

  getReferencePoint: function () {
    return this._referencePoint || undefined;
  },

  changed: function () {
    this._isChanged = true;
  },

  /**
   * Register a callback which will be called if the label
   * was rendered.
   * @param {Object} fn
   */
  registerOnChange: function (fn) {
    if (!this.changeCallbacks) {
      this.changeCallbacks = [];
    }
    if (fn instanceof Function && !this.changeCallbacks.include(fn)) {
      this.changeCallbacks.push(fn);
    }
  },

  /**
   * Unregister the callback for changes.
   * @param {Object} fn
   */
  unregisterOnChange: function (fn) {
    if (this.changeCallbacks && fn instanceof Function && this.changeCallbacks.include(fn)) {
      this.changeCallbacks = this.changeCallbacks.without(fn);
    }
  },

  /**
   * Returns TRUE if the labe is currently in
   * the update mechanism.
   * @return {Boolean}
   */
  isUpdating: function () {
    return !!this._isUpdating;
  },

  getOriginEdgePosition: function () {
    return this.originEdgePosition;
  },

  /**
   * Returns the edgeposition.
   *
   * @return {String} "starttop", "startmiddle", "startbottom",
   * "midtop", "midbottom", "endtop", "endbottom" or null
   */
  getEdgePosition: function () {
    return this.edgePosition || null;
  },

  /**
   * Set the edge position, must be one of the valid
   * edge positions (see getEdgePosition).
   * Removes the reference point and the absolute position as well.
   *
   * @param {Object} position
   */
  setEdgePosition: function (position) {
    if (['starttop', 'startmiddle', 'startbottom', 'midtop', 'midbottom', 'endtop', 'endbottom'].include(position)) {
      this.edgePosition = position;
      delete this.position;
      delete this._referencePoint;
    } else {
      delete this.edgePosition;
    }
  },

  /**
   * Update the SVG text element.
   */
  update: function (force) {
    var x = this.x,
      y = this.y;
    if (this.position) {
      x = this.position.x;
      y = this.position.y;
    }
    x = Math.floor(x);
    y = Math.floor(y);

    if (this._isChanged || x !== this.oldX || y !== this.oldY || force === true) {
      if (this.isVisible) {
        this._isChanged = false;
        this._isUpdating = true;

        this.node.setAttributeNS(null, 'x', x);
        this.node.setAttributeNS(null, 'y', y);
        this.node.removeAttributeNS(null, 'fill-opacity');

        //this.node.setAttributeNS(null, 'font-size', this._fontSize);
        //this.node.setAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align', this._horizontalAlign + " " + this._verticalAlign);

        this.oldX = x;
        this.oldY = y;

        //set rotation
        if (!this.position && !this.getReferencePoint()) {
          if (this._rotate !== undefined) {
            if (this._rotationPoint)
              this.node.setAttributeNS(
                null,
                'transform',
                'rotate(' + this._rotate + ' ' + Math.floor(this._rotationPoint.x) + ' ' + Math.floor(this._rotationPoint.y) + ')'
              );
            else this.node.setAttributeNS(null, 'transform', 'rotate(' + this._rotate + ' ' + Math.floor(x) + ' ' + Math.floor(y) + ')');
          }
        } else {
          this.node.removeAttributeNS(null, 'transform');
        }

        var textLines = this._text.split('\n');
        while (textLines.last() == '') textLines.pop();

        if (this.node.ownerDocument) {
          // Only reset the tspans if the text
          // has changed or has to be wrapped
          if (this.fitToElemId || this._textHasChanged) {
            this.node.textContent = ''; // Remove content
            textLines.each(
              function (textLine, index) {
                var tspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan');
                tspan.textContent = textLine.trim();
                if (this.fitToElemId) {
                  tspan.setAttributeNS(null, 'x', this.invisibleRenderPoint);
                  tspan.setAttributeNS(null, 'y', this.invisibleRenderPoint);
                }

                /*
                 * Chrome's getBBox() method fails, if a text node contains an empty tspan element.
                 * So, we add a whitespace to such a tspan element.
                 */
                if (tspan.textContent === '') {
                  tspan.textContent = ' ';
                }

                //append tspan to text node
                this.node.appendChild(tspan);
              }.bind(this)
            );
            delete this._textHasChanged;
            delete this.indices;
          }

          //Work around for Mozilla bug 293581
          if (this.isVisible && this.fitToElemId) {
            this.node.setAttributeNS(null, 'visibility', 'hidden');
          }

          if (this.fitToElemId) {
            window.setTimeout(this._checkFittingToReferencedElem.bind(this), 0);
            //this._checkFittingToReferencedElem();
          } else {
            window.setTimeout(this._positionText.bind(this), 0);
            //this._positionText();
          }
        }
      } else {
        this.node.textContent = '';
        //this.node.setAttributeNS(null, "fill-opacity", "0.2");
      }
    }
  },

  _checkFittingToReferencedElem: function () {
    try {
      var tspans = $A(this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan'));

      //only do this in firefox 3. all other browsers do not support word wrapping!!!!!
      //if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1)>=3) {
      var newtspans = [];

      var refNode = this.node.ownerDocument.getElementById(this.fitToElemId);

      if (refNode) {
        var refbb = refNode.getBBox();

        var fontSize = this.getFontSize();

        for (var j = 0; j < tspans.length; j++) {
          var tspan = tspans[j];

          var textLength = this._getRenderedTextLength(tspan, undefined, undefined, fontSize);

          var refBoxLength = this._rotate != 0 && this._rotate % 180 != 0 && this._rotate % 90 == 0 ? refbb.height : refbb.width;

          if (textLength > refBoxLength) {
            var startIndex = 0;
            var lastSeperatorIndex = 0;

            var numOfChars = this.getTrimmedTextLength(tspan.textContent);
            for (var i = 0; i < numOfChars; i++) {
              var sslength = this._getRenderedTextLength(tspan, startIndex, i - startIndex, fontSize);

              if (sslength > refBoxLength - 2) {
                var newtspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan');
                if (lastSeperatorIndex <= startIndex) {
                  lastSeperatorIndex = i == 0 ? i : i - 1;
                  newtspan.textContent = tspan.textContent.slice(startIndex, lastSeperatorIndex).trim();
                  //lastSeperatorIndex = i;
                } else {
                  newtspan.textContent = tspan.textContent.slice(startIndex, ++lastSeperatorIndex).trim();
                }

                newtspan.setAttributeNS(null, 'x', this.invisibleRenderPoint);
                newtspan.setAttributeNS(null, 'y', this.invisibleRenderPoint);

                //insert tspan to text node
                //this.node.insertBefore(newtspan, tspan);
                newtspans.push(newtspan);

                startIndex = lastSeperatorIndex;
              } else {
                var curChar = tspan.textContent.charAt(i);
                if (curChar == ' ' || curChar == '-' || curChar == '.' || curChar == ',' || curChar == ';' || curChar == ':') {
                  lastSeperatorIndex = i;
                }
              }
            }

            tspan.textContent = tspan.textContent.slice(startIndex).trim();
          }

          newtspans.push(tspan);
        }

        while (this.node.hasChildNodes()) this.node.removeChild(this.node.childNodes[0]);

        while (newtspans.length > 0) {
          this.node.appendChild(newtspans.shift());
        }
      }
      //}
    } catch (e) {
      ORYX.Log.fatal('Error ' + e);
    }
    window.setTimeout(this._positionText.bind(this), 0);
    //this._positionText();
  },

  /**
   * This is a work around method for Mozilla bug 293581.
   * Before the method getComputedTextLength works, the text has to be rendered.
   */
  _positionText: function () {
    try {
      var tspans = this.node.childNodes;

      var fontSize = this.getFontSize(this.node);

      var invalidTSpans = [];

      var x = this.x,
        y = this.y;
      if (this.position) {
        x = this.position.x;
        y = this.position.y;
      }
      x = Math.floor(x);
      y = Math.floor(y);

      var i = 0,
        indic = []; // Cache indices if the _positionText is called again, before update is called
      var is = this.indices || $R(0, tspans.length - 1).toArray();
      var length = is.length;
      is.each(
        function (index) {
          if ('undefined' == typeof index) {
            return;
          }

          var tspan = tspans[i++];

          if (tspan.textContent.trim() === '') {
            invalidTSpans.push(tspan);
          } else {
            //set vertical position
            var dy = 0;
            switch (this._verticalAlign) {
              case 'bottom':
                dy = -(length - index - 1) * fontSize;
                break;
              case 'middle':
                dy = -(length / 2.0 - index - 1) * fontSize;
                dy -= ORYX.CONFIG.LABEL_LINE_DISTANCE / 2;
                break;
              case 'top':
                dy = index * fontSize;
                dy += fontSize;
                break;
            }
            tspan.setAttributeNS(null, 'dy', Math.floor(dy));

            tspan.setAttributeNS(null, 'x', x);
            tspan.setAttributeNS(null, 'y', y);
            indic.push(index);
          }
        }.bind(this)
      );

      indic.length = tspans.length;
      this.indices = this.indices || indic;

      invalidTSpans.each(
        function (tspan) {
          this.node.removeChild(tspan);
        }.bind(this)
      );

      //set horizontal alignment
      switch (this._horizontalAlign) {
        case 'left':
          this.node.setAttributeNS(null, 'text-anchor', 'start');
          break;
        case 'center':
          this.node.setAttributeNS(null, 'text-anchor', 'middle');
          break;
        case 'right':
          this.node.setAttributeNS(null, 'text-anchor', 'end');
          break;
      }
    } catch (e) {
      //console.log(e);
      this._isChanged = true;
    }

    if (this.isVisible) {
      this.node.removeAttributeNS(null, 'visibility');
    }

    // Finished
    delete this._isUpdating;

    // Raise change event
    (this.changeCallbacks || []).each(function (fn) {
      fn.apply(fn);
    });
  },

  /**
   * Returns the text length of the text content of an SVG tspan element.
   * For all browsers but Firefox 3 the values are estimated.
   * @param {TSpanSVGElement} tspan
   * @param {int} startIndex Optional, for sub strings
   * @param {int} endIndex Optional, for sub strings
   */
  _getRenderedTextLength: function (tspan, startIndex, endIndex, fontSize) {
    //if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) {
    if (startIndex === undefined) {
      //test string: abcdefghijklmnopqrstuvwxyz????????????????,.-#+ 1234567890?????ABCDEFGHIJKLMNOPQRSTUVWXYZ;:_'*???????????????!"????$%&/()=?[]{}|<>'~????`\^?????????@?????????????????
      //				for(var i = 0; i < tspan.textContent.length; i++) {
      //					console.log(tspan.textContent.charAt(i), tspan.getSubStringLength(i,1), this._estimateCharacterWidth(tspan.textContent.charAt(i))*(fontSize/14.0));
      //				}
      return tspan.getComputedTextLength();
    } else {
      return tspan.getSubStringLength(startIndex, endIndex);
    }
    /*} else {
			if(startIndex === undefined) {
				return this._estimateTextWidth(tspan.textContent, fontSize);
			} else {
				return this._estimateTextWidth(tspan.textContent.substr(startIndex, endIndex).trim(), fontSize);
			}
		}*/
  },

  /**
   * Estimates the text width for a string.
   * Used for word wrapping in all browser but FF3.
   * @param {Object} text
   */
  _estimateTextWidth: function (text, fontSize) {
    var sum = 0.0;
    for (var i = 0; i < text.length; i++) {
      sum += this._estimateCharacterWidth(text.charAt(i));
    }

    return sum * (fontSize / 14.0);
  },

  /**
   * Estimates the width of a single character for font size 14.
   * Used for word wrapping in all browser but FF3.
   * @param {Object} character
   */
  _estimateCharacterWidth: function (character) {
    for (var i = 0; i < this._characterSets.length; i++) {
      if (this._characterSets[i].indexOf(character) >= 0) {
        return this._characterSetValues[i];
      }
    }
    return 9;
  },

  getReferencedElementWidth: function () {
    var refNode = this.node.ownerDocument.getElementById(this.fitToElemId);

    if (refNode) {
      var refbb = refNode.getBBox();

      if (refbb) {
        return this._rotate != 0 && this._rotate % 180 != 0 && this._rotate % 90 == 0 ? refbb.height : refbb.width;
      }
    }

    return undefined;
  },

  /**
   * If no parameter is provided, this method returns the current text.
   * @param text {String} Optional. Replaces the old text with this one.
   */
  text: function () {
    switch (arguments.length) {
      case 0:
        return this._text;
        break;

      case 1:
        var oldText = this._text;
        if (arguments[0]) {
          // Filter out multiple spaces to fix issue in chrome for line-wrapping
          this._text = arguments[0].toString();
          if (this._text != null && this._text != undefined) {
            this._text = this._text.replace(/ {2,}/g, ' ');
          }
        } else {
          this._text = '';
        }
        if (oldText !== this._text) {
          this._isChanged = true;
          this._textHasChanged = true;
        }
        break;

      default:
        //TODO error
        break;
    }
  },

  getOriginVerticalAlign: function () {
    return this._originVerticalAlign;
  },

  verticalAlign: function () {
    switch (arguments.length) {
      case 0:
        return this._verticalAlign;
      case 1:
        if (['top', 'middle', 'bottom'].member(arguments[0])) {
          var oldValue = this._verticalAlign;
          this._verticalAlign = arguments[0];
          if (this._verticalAlign !== oldValue) {
            this._isChanged = true;
          }
        }
        break;

      default:
        //TODO error
        break;
    }
  },

  getOriginHorizontalAlign: function () {
    return this._originHorizontalAlign;
  },

  horizontalAlign: function () {
    switch (arguments.length) {
      case 0:
        return this._horizontalAlign;
      case 1:
        if (['left', 'center', 'right'].member(arguments[0])) {
          var oldValue = this._horizontalAlign;
          this._horizontalAlign = arguments[0];
          if (this._horizontalAlign !== oldValue) {
            this._isChanged = true;
          }
        }
        break;

      default:
        //TODO error
        break;
    }
  },

  rotate: function () {
    switch (arguments.length) {
      case 0:
        return this._rotate;
      case 1:
        if (this._rotate != arguments[0]) {
          this._rotate = arguments[0];
          this._rotationPoint = undefined;
          this._isChanged = true;
        }
      case 2:
        if (
          this._rotate != arguments[0] ||
          !this._rotationPoint ||
          this._rotationPoint.x != arguments[1].x ||
          this._rotationPoint.y != arguments[1].y
        ) {
          this._rotate = arguments[0];
          this._rotationPoint = arguments[1];
          this._isChanged = true;
        }
    }
  },

  hide: function () {
    if (this.isVisible) {
      this.isVisible = false;
      this._isChanged = true;
    }
  },

  show: function () {
    if (!this.isVisible) {
      this.isVisible = true;
      this._isChanged = true;

      // Since text is removed from the tspan when "hidden", mark
      // the text as changed to get it redrawn
      this._textHasChanged = true;
    }
  },

  /**
   * iterates parent nodes till it finds a SVG font-size
   * attribute.
   * @param {SVGElement} node
   */
  getInheritedFontSize: function (node) {
    if (!node || !node.getAttributeNS) return;

    var attr = node.getAttributeNS(null, 'font-size');
    if (attr) {
      return parseFloat(attr);
    } else if (!ORYX.Editor.checkClassType(node, SVGSVGElement)) {
      return this.getInheritedFontSize(node.parentNode);
    }
  },

  getFontSize: function (node) {
    var tspans = this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan');

    //trying to get an inherited font-size attribute
    //NO CSS CONSIDERED!
    var fontSize = this.getInheritedFontSize(this.node);

    if (!fontSize) {
      //because this only works in firefox 3, all other browser use the default line height
      if (tspans[0] && /Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) {
        fontSize = tspans[0].getExtentOfChar(0).height;
      } else {
        fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT;
      }

      //handling of unsupported method in webkit
      if (fontSize <= 0) {
        fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT;
      }
    }

    if (fontSize) this.node.setAttribute('oryx:fontSize', fontSize);

    return fontSize;
  },

  /**
   * Get trimmed text length for use with
   * getExtentOfChar and getSubStringLength.
   * @param {String} text
   */
  getTrimmedTextLength: function (text) {
    text = text.strip().gsub('  ', ' ');

    var oldLength;
    do {
      oldLength = text.length;
      text = text.gsub('  ', ' ');
    } while (oldLength > text.length);

    return text.length;
  },

  /**
   * Returns the offset from
   * edge to the label which is
   * positioned under the edge
   * @return {int}
   */
  getOffsetBottom: function () {
    return this.offsetBottom;
  },

  /**
   * Returns the offset from
   * edge to the label which is
   * positioned over the edge
   * @return {int}
   */
  getOffsetTop: function () {
    return this.offsetTop;
  },

  /**
   *
   * @param {Object} obj
   */
  deserialize: function (obj, shape) {
    if (obj && 'undefined' != typeof obj.x && 'undefined' != typeof obj.y) {
      this.setPosition({ x: obj.x, y: obj.y });

      if ('undefined' != typeof obj.distance) {
        var from = shape.dockers[obj.from];
        var to = shape.dockers[obj.to];
        if (from && to) {
          this.setReferencePoint({
            dirty: true,
            distance: obj.distance,
            intersection: { x: obj.x, y: obj.y },
            orientation: obj.orientation,
            segment: {
              from: from,
              fromIndex: obj.from,
              fromPosition: from.bounds.center(),
              to: to,
              toIndex: obj.to,
              toPosition: to.bounds.center(),
            },
          });
        }
      }

      if (obj.left) this.anchorLeft = true;
      if (obj.right) this.anchorRight = true;
      if (obj.top) this.anchorTop = true;
      if (obj.bottom) this.anchorBottom = true;
      if (obj.valign) this.verticalAlign(obj.valign);
      if (obj.align) this.horizontalAlign(obj.align);
    } else if (obj && 'undefined' != typeof obj.edge) {
      this.setEdgePosition(obj.edge);
    }
  },

  /**
   *
   * @return {Object}
   */
  serialize: function () {
    // On edge position
    if (this.getEdgePosition()) {
      if (this.getOriginEdgePosition() !== this.getEdgePosition()) {
        return { edge: this.getEdgePosition() };
      } else {
        return null;
      }
    }

    // On self defined position
    if (this.position) {
      var pos = { x: this.position.x, y: this.position.y };
      if (this.isAnchorLeft() && this.isAnchorLeft() !== this.isOriginAnchorLeft()) {
        pos.left = true;
      }
      if (this.isAnchorRight() && this.isAnchorRight() !== this.isOriginAnchorRight()) {
        pos.right = true;
      }
      if (this.isAnchorTop() && this.isAnchorTop() !== this.isOriginAnchorTop()) {
        pos.top = true;
      }
      if (this.isAnchorBottom() && this.isAnchorBottom() !== this.isOriginAnchorBottom()) {
        pos.bottom = true;
      }

      if (this.getOriginVerticalAlign() !== this.verticalAlign()) {
        pos.valign = this.verticalAlign();
      }
      if (this.getOriginHorizontalAlign() !== this.horizontalAlign()) {
        pos.align = this.horizontalAlign();
      }

      return pos;
    }

    // On reference point which is interesting for edges
    if (this.getReferencePoint()) {
      var ref = this.getReferencePoint();
      return {
        distance: ref.distance,
        x: ref.intersection.x,
        y: ref.intersection.y,
        from: ref.segment.fromIndex,
        to: ref.segment.toIndex,
        orientation: ref.orientation,
        valign: this.verticalAlign(),
        align: this.horizontalAlign(),
      };
    }
    return null;
  },

  toString: function () {
    return 'Label ' + this.id;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.Math) {
  ORYX.Core.Math = {};
}

/**
 * Calculate the middle point between two given points
 * @param {x:double, y:double} point1
 * @param {x:double, y:double} point2
 * @return the middle point
 */
ORYX.Core.Math.midPoint = function (point1, point2) {
  return {
    x: (point1.x + point2.x) / 2.0,
    y: (point1.y + point2.y) / 2.0,
  };
};

/**
 * Returns a TRUE if the point is over a line (defined by
 * point1 and point 2). In Addition a threshold can be set,
 * which defines the weight of those line.
 *
 * @param {int} pointX - Point X
 * @param {int} pointY - Point Y
 * @param {int} lPoint1X - Line first Point X
 * @param {int} lPoint1Y - Line first Point Y
 * @param {int} lPoint2X - Line second Point X
 * @param {int} lPoint2Y - Line second Point y
 * @param {int} offset {optional} - maximal distance to line
 * @class ORYX.Core.Math.prototype
 */
ORYX.Core.Math.isPointInLine = function (pointX, pointY, lPoint1X, lPoint1Y, lPoint2X, lPoint2Y, offset) {
  offset = offset ? Math.abs(offset) : 1;

  // Check if the edge is vertical
  if (
    Math.abs(lPoint1X - lPoint2X) <= offset &&
    Math.abs(pointX - lPoint1X) <= offset &&
    pointY - Math.max(lPoint1Y, lPoint2Y) <= offset &&
    Math.min(lPoint1Y, lPoint2Y) - pointY <= offset
  ) {
    return true;
  }

  // Check if the edge is horizontal
  if (
    Math.abs(lPoint1Y - lPoint2Y) <= offset &&
    Math.abs(pointY - lPoint1Y) <= offset &&
    pointX - Math.max(lPoint1X, lPoint2X) <= offset &&
    Math.min(lPoint1X, lPoint2X) - pointX <= offset
  ) {
    return true;
  }

  if (pointX > Math.max(lPoint1X, lPoint2X) || pointX < Math.min(lPoint1X, lPoint2X)) {
    return false;
  }

  if (pointY > Math.max(lPoint1Y, lPoint2Y) || pointY < Math.min(lPoint1Y, lPoint2Y)) {
    return false;
  }

  var s = (lPoint1Y - lPoint2Y) / (lPoint1X - lPoint2X);

  return Math.abs(pointY - (s * pointX + lPoint1Y - s * lPoint1X)) < offset;
};

/**
 * Get a boolean if the point is in the polygone
 *
 */
ORYX.Core.Math.isPointInEllipse = function (pointX, pointY, cx, cy, rx, ry) {
  if (cx === undefined || cy === undefined || rx === undefined || ry === undefined) {
    throw 'ORYX.Core.Math.isPointInEllipse needs a ellipse with these properties: x, y, radiusX, radiusY';
  }

  var tx = (pointX - cx) / rx;
  var ty = (pointY - cy) / ry;

  return tx * tx + ty * ty < 1.0;
};

/**
 * Get a boolean if the point is in the polygone
 * @param {int} pointX
 * @param {int} pointY
 * @param {[int]} Cornerpoints of the Polygone (x,y,x,y,...)
 */
ORYX.Core.Math.isPointInPolygone = function (pointX, pointY, polygone) {
  if (arguments.length < 3) {
    throw 'ORYX.Core.Math.isPointInPolygone needs two arguments';
  }

  var lastIndex = polygone.length - 1;

  if (polygone[0] !== polygone[lastIndex - 1] || polygone[1] !== polygone[lastIndex]) {
    polygone.push(polygone[0]);
    polygone.push(polygone[1]);
  }

  var crossings = 0;

  var x1, y1, x2, y2, d;

  for (var i = 0; i < polygone.length - 3; ) {
    x1 = polygone[i];
    y1 = polygone[++i];
    x2 = polygone[++i];
    y2 = polygone[i + 1];
    d = (pointY - y1) * (x2 - x1) - (pointX - x1) * (y2 - y1);

    if (y1 >= pointY != y2 >= pointY) {
      crossings += y2 - y1 >= 0 ? d >= 0 : d <= 0;
    }
    if (!d && Math.min(x1, x2) <= pointX && pointX <= Math.max(x1, x2) && Math.min(y1, y2) <= pointY && pointY <= Math.max(y1, y2)) {
      return true;
    }
  }
  return crossings % 2 ? true : false;
};

/**
 *	Calculates the distance between a point and a line. It is also testable, if
 *  the distance orthogonal to the line, matches the segment of the line.
 *
 *  @param {float} lineP1
 *  	The starting point of the line segment
 *  @param {float} lineP2
 *  	The end point of the line segment
 *  @param {Point} point
 *  	The point to calculate the distance to.
 *  @param {boolean} toSegmentOnly
 *  	Flag to signal if only the segment of the line shell be evaluated.
 */
ORYX.Core.Math.distancePointLinie = function (lineP1, lineP2, point, toSegmentOnly) {
  var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine(lineP1, lineP2, point, toSegmentOnly);

  if (!intersectionPoint) {
    return null;
  }

  return ORYX.Core.Math.getDistancePointToPoint(point, intersectionPoint);
};

/**
 * Calculates the distance between two points.
 *
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getDistancePointToPoint = function (point1, point2) {
  return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
};

/**
 * Calculates the relative distance of a point which is between two other points.
 *
 * @param {point} between1
 * @param {point} between2
 * @param {point} point
 */
ORYX.Core.Math.getDistanceBetweenTwoPoints = function (between1, between2, point) {
  return ORYX.Core.Math.getDistancePointToPoint(point, between1) / ORYX.Core.Math.getDistancePointToPoint(between1, between2);
};

/**
 * Returns true, if the point is of the left hand
 * side of the regarding the line.
 *
 * @param {point} lineP1 Line first point
 * @param {point} lineP2 Line second point
 * @param {point} point
 */
ORYX.Core.Math.pointIsLeftOfLine = function (lineP1, lineP2, point) {
  var vec1 = ORYX.Core.Math.getVector(lineP1, lineP2);
  var vec2 = ORYX.Core.Math.getVector(lineP1, point);
  // if the cross produkt is more than 0
  return vec1.x * vec2.y - vec2.x * vec1.y > 0;
};

/**
 * Calculates the a point which is relatively between two other points.
 *
 * @param {point} point1
 * @param {point} point2
 * @param {number} relative Relative which is between 0 and 1
 */
ORYX.Core.Math.getPointBetweenTwoPoints = function (point1, point2, relative) {
  relative = Math.max(Math.min(relative || 0, 1), 0);

  if (relative === 0) {
    return point1;
  } else if (relative === 1) {
    return point2;
  }

  return {
    x: point1.x + (point2.x - point1.x) * relative,
    y: point1.y + (point2.y - point1.y) * relative,
  };
};

/**
 * Returns the vector of the both points
 *
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getVector = function (point1, point2) {
  return {
    x: point2.x - point1.x,
    y: point2.y - point1.y,
  };
};

/**
 * Returns the an identity vector of the given vector,
 * which has the length ot one.
 *
 * @param {point} vector
 * or
 * @param {point} point1
 * @param {point} point2
 */
ORYX.Core.Math.getIdentityVector = function (vector) {
  if (arguments.length == 2) {
    vector = ORYX.Core.Math.getVector(arguments[0], arguments[1]);
  }

  var length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
  return {
    x: vector.x / (length || 1),
    y: vector.y / (length || 1),
  };
};

ORYX.Core.Math.getOrthogonalIdentityVector = function (point1, point2) {
  var vec = arguments.length == 1 ? point1 : ORYX.Core.Math.getIdentityVector(point1, point2);
  return {
    x: vec.y,
    y: -vec.x,
  };
};

/**
 * Returns the intersection point of a line and a point that defines a line
 * orthogonal to the given line.
 *
 *  @param {float} lineP1
 *  	The starting point of the line segment
 *  @param {float} lineP2
 *  	The end point of the line segment
 *  @param {Point} point
 *  	The point to calculate the distance to.
 *  @param {boolean} onSegmentOnly
 *  	Flag to signal if only the segment of the line shell be evaluated.
 */
ORYX.Core.Math.getPointOfIntersectionPointLine = function (lineP1, lineP2, point, onSegmentOnly) {
  /*
   * [P3 - P1 - u(P2 - P1)] dot (P2 - P1) = 0
   * u =((x3-x1)(x2-x1)+(y3-y1)(y2-y1))/(p2-p1)??
   */
  var denominator = Math.pow(lineP2.x - lineP1.x, 2) + Math.pow(lineP2.y - lineP1.y, 2);
  if (denominator == 0) {
    return undefined;
  }

  var u = ((point.x - lineP1.x) * (lineP2.x - lineP1.x) + (point.y - lineP1.y) * (lineP2.y - lineP1.y)) / denominator;

  if (onSegmentOnly) {
    if (!(0 <= u && u <= 1)) {
      return undefined;
    }
  }

  pointOfIntersection = new Object();
  pointOfIntersection.x = lineP1.x + u * (lineP2.x - lineP1.x);
  pointOfIntersection.y = lineP1.y + u * (lineP2.y - lineP1.y);

  return pointOfIntersection;
};

/**
 * Translated the point with the given matrix.
 * @param {Point} point
 * @param {Matrix} matrix
 * @return {Object} Includes x, y
 */
ORYX.Core.Math.getTranslatedPoint = function (point, matrix) {
  var x = matrix.a * point.x + matrix.c * point.y + matrix.e * 1;
  var y = matrix.b * point.x + matrix.d * point.y + matrix.f * 1;
  return { x: x, y: y };
};

/**
 * Returns the inverse matrix of the given SVG transformation matrix
 * @param {SVGTransformationMatrix} matrix
 * @return {Matrix}
 */
ORYX.Core.Math.getInverseMatrix = function (matrix) {
  var det = ORYX.Core.Math.getDeterminant(matrix),
    m = matrix;
  // +-     -+
  // | a c e |
  // | b d f |
  // | 0 0 1 |
  // +-     -+
  return {
    a: det * (m.d * 1 - m.f * 0),
    b: det * (m.f * 0 - m.b * 1),
    c: det * (m.e * 0 - m.c * 1),
    d: det * (m.a * 1 - m.e * 0),
    e: det * (m.c * m.f - m.e * m.d),
    f: det * (m.e * m.b - m.a * m.f),
  };
};

/**
 * Returns the determinant of the svg transformation matrix
 * @param {SVGTranformationMatrix} matrix
 * @return {Number}
 *
 */
ORYX.Core.Math.getDeterminant = function (m) {
  // a11a22a33+a12a23a31+a13a21a32-a13a22a31-a12a21a33-a11a23a32
  return m.a * m.d * 1 + m.c * m.f * 0 + m.e * m.b * 0 - m.e * m.d * 0 - m.c * m.b * 1 - m.a * m.f * 0;
};

/**
 * Returns the bounding box of the given node. Translates the
 * origin bounding box with the tranlation matrix.
 * @param {SVGElement} node
 * @return {Object} Includes x, y, width, height
 */
ORYX.Core.Math.getTranslatedBoundingBox = function (node) {
  var matrix = node.getCTM();
  var bb = node.getBBox();
  var ul = ORYX.Core.Math.getTranslatedPoint({ x: bb.x, y: bb.y }, matrix);
  var ll = ORYX.Core.Math.getTranslatedPoint({ x: bb.x, y: bb.y + bb.height }, matrix);
  var ur = ORYX.Core.Math.getTranslatedPoint({ x: bb.x + bb.width, y: bb.y }, matrix);
  var lr = ORYX.Core.Math.getTranslatedPoint({ x: bb.x + bb.width, y: bb.y + bb.height }, matrix);

  var minPoint = {
    x: Math.min(ul.x, ll.x, ur.x, lr.x),
    y: Math.min(ul.y, ll.y, ur.y, lr.y),
  };
  var maxPoint = {
    x: Math.max(ul.x, ll.x, ur.x, lr.x),
    y: Math.max(ul.y, ll.y, ur.y, lr.y),
  };
  return {
    x: minPoint.x,
    y: minPoint.y,
    width: maxPoint.x - minPoint.x,
    height: maxPoint.y - minPoint.y,
  };
};

/**
 * Returns the angle of the given line, which is representated by the two points
 * @param {Point} p1
 * @param {Point} p2
 * @return {Number} 0 <= x <= 359.99999
 */
ORYX.Core.Math.getAngle = function (p1, p2) {
  if (p1.x == p2.x && p1.y == p2.y) return 0;

  var angle =
    (Math.asin(Math.sqrt(Math.pow(p1.y - p2.y, 2)) / Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p1.y - p2.y, 2))) * 180) / Math.PI;

  if (p2.x >= p1.x && p2.y <= p1.y) return angle;
  else if (p2.x < p1.x && p2.y <= p1.y) return 180 - angle;
  else if (p2.x < p1.x && p2.y > p1.y) return 180 + angle;
  else return 360 - angle;
};

/**
 * Implementation of the cohen-sutherland algorithm
 */
new (function () {
  var RIGHT = 2,
    TOP = 8,
    BOTTOM = 4,
    LEFT = 1;

  function computeOutCode(x, y, xmin, ymin, xmax, ymax) {
    var code = 0;
    if (y > ymax) code |= TOP;
    else if (y < ymin) code |= BOTTOM;
    if (x > xmax) code |= RIGHT;
    else if (x < xmin) code |= LEFT;
    return code;
  }

  /**
   * Returns TRUE if the rectangle is over the edge and has intersection points or includes it
   * @param {Object} x1 Point A of the line
   * @param {Object} y1
   * @param {Object} x2 Point B of the line
   * @param {Object} y2
   * @param {Object} xmin Point A of the rectangle
   * @param {Object} ymin
   * @param {Object} xmax Point B of the rectangle
   * @param {Object} ymax
   */
  ORYX.Core.Math.isRectOverLine = function (x1, y1, x2, y2, xmin, ymin, xmax, ymax) {
    return !!ORYX.Core.Math.clipLineOnRect.apply(ORYX.Core.Math, arguments);
  };

  /**
   * Returns the clipped line on the given rectangle. If there is
   * no intersection, it will return NULL.
   *
   * @param {Object} x1 Point A of the line
   * @param {Object} y1
   * @param {Object} x2 Point B of the line
   * @param {Object} y2
   * @param {Object} xmin Point A of the rectangle
   * @param {Object} ymin
   * @param {Object} xmax Point B of the rectangle
   * @param {Object} ymax
   */
  ORYX.Core.Math.clipLineOnRect = function (x1, y1, x2, y2, xmin, ymin, xmax, ymax) {
    //Outcodes for P0, P1, and whatever point lies outside the clip rectangle
    var outcode0,
      outcode1,
      outcodeOut,
      hhh = 0;
    var accept = false,
      done = false;

    //compute outcodes
    outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax);
    outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax);

    do {
      if ((outcode0 | outcode1) == 0) {
        accept = true;
        done = true;
      } else if ((outcode0 & outcode1) > 0) {
        done = true;
      } else {
        //failed both tests, so calculate the line segment to clip
        //from an outside point to an intersection with clip edge
        var x = 0,
          y = 0;
        //At least one endpoint is outside the clip rectangle; pick it.
        outcodeOut = outcode0 != 0 ? outcode0 : outcode1;
        //Now find the intersection point;
        //use formulas y = y0 + slope * (x - x0), x = x0 + (1/slope)* (y - y0)
        if ((outcodeOut & TOP) > 0) {
          x = x1 + ((x2 - x1) * (ymax - y1)) / (y2 - y1);
          y = ymax;
        } else if ((outcodeOut & BOTTOM) > 0) {
          x = x1 + ((x2 - x1) * (ymin - y1)) / (y2 - y1);
          y = ymin;
        } else if ((outcodeOut & RIGHT) > 0) {
          y = y1 + ((y2 - y1) * (xmax - x1)) / (x2 - x1);
          x = xmax;
        } else if ((outcodeOut & LEFT) > 0) {
          y = y1 + ((y2 - y1) * (xmin - x1)) / (x2 - x1);
          x = xmin;
        }

        //Now we move outside point to intersection point to clip
        //and get ready for next pass.
        if (outcodeOut == outcode0) {
          x1 = x;
          y1 = y;
          outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax);
        } else {
          x2 = x;
          y2 = y;
          outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax);
        }
      }
      hhh++;
    } while (done != true && hhh < 5000);

    if (accept) {
      return { a: { x: x1, y: y1 }, b: { x: x2, y: y2 } };
    }
    return null;
  };
})();

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 *
 * This class represents one stencil of a stencil set.
 */
ORYX.Core.StencilSet.Stencil = {
  /**
   * Constructor
   */
  construct: function (jsonStencil, namespace, source, stencilSet, propertyPackages, defaultPosition) {
    arguments.callee.$.construct.apply(this, arguments); // super();

    // check arguments and set defaults.
    if (!jsonStencil) throw 'Stencilset seems corrupt.';
    if (!namespace) throw 'Stencil does not provide namespace.';
    if (!source) throw 'Stencil does not provide SVG source.';
    if (!stencilSet) throw 'Fatal internal error loading stencilset.';
    //if(!propertyPackages) throw "Fatal internal error loading stencilset.";

    this._source = source;
    this._jsonStencil = jsonStencil;
    this._stencilSet = stencilSet;
    this._namespace = namespace;
    this._propertyPackages = propertyPackages;

    if (defaultPosition && !this._jsonStencil.position) this._jsonStencil.position = defaultPosition;

    this._view;
    this._properties = new Hash();

    // check stencil consistency and set defaults.
    /*with(this._jsonStencil) {
			
			if(!type) throw "Stencil does not provide type.";
			if((type != "edge") && (type != "node"))
				throw "Stencil type must be 'edge' or 'node'.";
			if(!id || id == "") throw "Stencil does not provide valid id.";
			if(!title || title == "")
				throw "Stencil does not provide title";
			if(!description) { description = ""; };
			if(!groups) { groups = []; }
			if(!roles) { roles = []; }

			// add id of stencil to its roles
			roles.push(id);
		}*/

    //init all JSON values
    if (!this._jsonStencil.type || !(this._jsonStencil.type === 'edge' || this._jsonStencil.type === 'node')) {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Type is not defined.';
    }
    if (!this._jsonStencil.id || this._jsonStencil.id === '') {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Id is not defined.';
    }
    if (!this._jsonStencil.title || this._jsonStencil.title === '') {
      throw 'ORYX.Core.StencilSet.Stencil(construct): Title is not defined.';
    }

    if (!this._jsonStencil.description) {
      this._jsonStencil.description = '';
    }
    if (!this._jsonStencil.groups) {
      this._jsonStencil.groups = [];
    }
    if (!this._jsonStencil.roles) {
      this._jsonStencil.roles = [];
    }

    //add id of stencil to its roles
    this._jsonStencil.roles.push(this._jsonStencil.id);

    //prepend namespace to each role
    this._jsonStencil.roles.each(
      function (role, index) {
        this._jsonStencil.roles[index] = namespace + role;
      }.bind(this)
    );

    //delete duplicate roles
    this._jsonStencil.roles = this._jsonStencil.roles.uniq();

    //make id unique by prepending namespace of stencil set
    this._jsonStencil.id = namespace + this._jsonStencil.id;

    this.postProcessProperties();

    // init serialize callback
    if (!this._jsonStencil.serialize) {
      this._jsonStencil.serialize = {};
      //this._jsonStencil.serialize = function(shape, data) { return data;};
    }

    // init deserialize callback
    if (!this._jsonStencil.deserialize) {
      this._jsonStencil.deserialize = {};
      //this._jsonStencil.deserialize = function(shape, data) { return data;};
    }

    // init layout callback
    if (!this._jsonStencil.layout) {
      this._jsonStencil.layout = [];
      //this._jsonStencil.layout = function() {return true;}
    }

    //TODO does not work correctly, if the url does not exist
    //How to guarantee that the view is loaded correctly before leaving the constructor???
    var url = source + 'view/' + jsonStencil.view;
    // override content type when this is webkit.

    if (this._jsonStencil.view.trim().match(/</)) {
      var parser = new DOMParser();
      var xml = parser.parseFromString(this._jsonStencil.view, 'text/xml');

      //check if result is a SVG document
      if (ORYX.Editor.checkClassType(xml.documentElement, SVGSVGElement)) {
        this._view = xml.documentElement;
      } else {
        throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document.';
      }
    } else {
      new Ajax.Request(url, {
        asynchronous: false,
        method: 'get',
        onSuccess: this._loadSVGOnSuccess.bind(this),
        onFailure: this._loadSVGOnFailure.bind(this),
      });
    }
  },

  postProcessProperties: function () {
    // init property packages
    if (this._jsonStencil.propertyPackages && this._jsonStencil.propertyPackages instanceof Array) {
      this._jsonStencil.propertyPackages.each(
        function (ppId) {
          var pp = this._propertyPackages[ppId];

          if (pp) {
            pp.each(
              function (prop) {
                var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this);
                this._properties[oProp.prefix() + '-' + oProp.id()] = oProp;
              }.bind(this)
            );
          }
        }.bind(this)
      );
    }

    // init properties
    if (this._jsonStencil.properties && this._jsonStencil.properties instanceof Array) {
      this._jsonStencil.properties.each(
        function (prop) {
          var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this);
          this._properties[oProp.prefix() + '-' + oProp.id()] = oProp;
        }.bind(this)
      );
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.Stencil} stencil
   * @return {Boolean} True, if stencil has the same namespace and type.
   */
  equals: function (stencil) {
    return this.id() === stencil.id();
  },

  stencilSet: function () {
    return this._stencilSet;
  },

  type: function () {
    return this._jsonStencil.type;
  },

  namespace: function () {
    return this._namespace;
  },

  id: function () {
    return this._jsonStencil.id;
  },

  idWithoutNs: function () {
    return this.id().replace(this.namespace(), '');
  },

  title: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'title');
  },

  description: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'description');
  },

  groups: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, 'groups');
  },

  position: function () {
    return isNaN(this._jsonStencil.position) ? 0 : this._jsonStencil.position;
  },

  view: function () {
    return this._view.cloneNode(true) || this._view;
  },

  icon: function () {
    return this._jsonStencil.icon;
  },

  fixedAspectRatio: function () {
    return this._jsonStencil.fixedAspectRatio === true;
  },

  hasMultipleRepositoryEntries: function () {
    return this.getRepositoryEntries().length > 0;
  },

  getRepositoryEntries: function () {
    return this._jsonStencil.repositoryEntries ? $A(this._jsonStencil.repositoryEntries) : $A([]);
  },

  properties: function () {
    return this._properties.values();
  },

  property: function (id) {
    return this._properties[id];
  },

  roles: function () {
    return this._jsonStencil.roles;
  },

  defaultAlign: function () {
    if (!this._jsonStencil.defaultAlign) return 'east';
    return this._jsonStencil.defaultAlign;
  },

  serialize: function (shape, data) {
    return this._jsonStencil.serialize;
    //return this._jsonStencil.serialize(shape, data);
  },

  deserialize: function (shape, data) {
    return this._jsonStencil.deserialize;
    //return this._jsonStencil.deserialize(shape, data);
  },

  // in which case is targetShape used?
  //	layout: function(shape, targetShape) {
  //		return this._jsonStencil.layout(shape, targetShape);
  //	},
  // layout property to store events for layouting in plugins
  layout: function (shape) {
    return this._jsonStencil.layout;
  },

  addProperty: function (property, namespace) {
    if (property && namespace) {
      var oProp = new ORYX.Core.StencilSet.Property(property, namespace, this);
      this._properties[oProp.prefix() + '-' + oProp.id()] = oProp;
    }
  },

  removeProperty: function (propertyId) {
    if (propertyId) {
      var oProp = this._properties.values().find(function (prop) {
        return propertyId == prop.id();
      });
      if (oProp) delete this._properties[oProp.prefix() + '-' + oProp.id()];
    }
  },

  _loadSVGOnSuccess: function (result) {
    var xml = null;

    /*
     * We want to get a dom object for the requested file. Unfortunately,
     * safari has some issues here. this is meant as a fallback for all
     * browsers that don't recognize the svg mimetype as XML but support
     * data: urls on Ajax calls.
     */

    // responseXML != undefined.
    // if(!(result.responseXML))

    // get the dom by data: url.
    // xml = _evenMoreEvilHack(result.responseText, 'text/xml');

    // else

    // get it the usual way.
    xml = result.responseXML;

    //check if result is a SVG document
    if (ORYX.Editor.checkClassType(xml.documentElement, SVGSVGElement)) {
      this._view = xml.documentElement;
    } else {
      throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document.';
    }
  },

  _loadSVGOnFailure: function (result) {
    throw 'ORYX.Core.StencilSet.Stencil(_loadSVGOnFailure): Loading SVG document failed.';
  },

  toString: function () {
    return 'Stencil ' + this.title() + ' (' + this.id() + ')';
  },
};

ORYX.Core.StencilSet.Stencil = Clazz.extend(ORYX.Core.StencilSet.Stencil);

/**
 * Transform a string into an xml document, the Safari way, as long as
 * the nightlies are broken. Even more evil version.
 * @param {Object} str
 * @param {Object} contentType
 */
function _evenMoreEvilHack(str, contentType) {
  /*
   * This even more evil hack was taken from
   * http://web-graphics.com/mtarchive/001606.php#chatty004999
   */

  if (window.ActiveXObject) {
    var d = new ActiveXObject('MSXML.DomDocument');
    d.loadXML(str);
    return d;
  } else if (window.XMLHttpRequest) {
    var req = new XMLHttpRequest();
    req.open('GET', 'data:' + (contentType || 'application/xml') + ';charset=utf-8,' + encodeURIComponent(str), false);
    if (req.overrideMimeType) {
      req.overrideMimeType(contentType);
    }
    req.send(null);
    return req.responseXML;
  }
}

/**
 * Transform a string into an xml document, the Safari way, as long as
 * the nightlies are broken.
 * @param {Object} result the xml document object.
 */
function _evilSafariHack(serializedXML) {
  /*
   *  The Dave way. Taken from:
   *  http://web-graphics.com/mtarchive/001606.php
   *
   *  There is another possibility to parse XML in Safari, by implementing
   *  the DOMParser in javascript. However, in the latest nightlies of
   *  WebKit, DOMParser is already available, but still buggy. So, this is
   *  the best compromise for the time being.
   */

  var xml = serializedXML;
  var url = 'data:text/xml;charset=utf-8,' + encodeURIComponent(xml);
  var dom = null;

  // your standard AJAX stuff
  var req = new XMLHttpRequest();
  req.open('GET', url);
  req.onload = function () {
    dom = req.responseXML;
  };
  req.send(null);

  return dom;
}
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class Property
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.Property = Clazz.extend({
  /**
   * Constructor
   */
  construct: function (jsonProp, namespace, stencil) {
    arguments.callee.$.construct.apply(this, arguments);

    this._jsonProp = jsonProp || ORYX.Log.error('Parameter jsonProp is not defined.');
    this._namespace = namespace || ORYX.Log.error('Parameter namespace is not defined.');
    this._stencil = stencil || ORYX.Log.error('Parameter stencil is not defined.');

    this._items = {};
    this._complexItems = {};

    // Flag to indicate whether or not the property should be hidden
    // This can be for example when the stencil set is upgraded, but the model
    // has a value for that specific property filled in which we still want to show.
    // If the value is missing, the property can simply be not shown.
    this._hidden = false;

    jsonProp.id = jsonProp.id || ORYX.Log.error('ORYX.Core.StencilSet.Property(construct): Id is not defined.');
    jsonProp.id = jsonProp.id.toLowerCase();

    if (!jsonProp.type) {
      ORYX.Log.info("Type is not defined for stencil '%0', id '%1'. Falling back to 'String'.", stencil, jsonProp.id);
      jsonProp.type = 'string';
    } else {
      jsonProp.type = jsonProp.type.toLowerCase();
    }

    jsonProp.prefix = jsonProp.prefix || 'oryx';
    jsonProp.title = jsonProp.title || '';
    jsonProp.value = jsonProp.value || '';
    jsonProp.description = jsonProp.description || '';
    jsonProp.readonly = jsonProp.readonly || false;
    jsonProp.optional = jsonProp.optional !== false;

    //init refToView
    if (this._jsonProp.refToView) {
      if (!(this._jsonProp.refToView instanceof Array)) {
        this._jsonProp.refToView = [this._jsonProp.refToView];
      }
    } else {
      this._jsonProp.refToView = [];
    }

    var globalMin = this.getMinForType(jsonProp.type);
    if (jsonProp.min === undefined || jsonProp.min === null) {
      jsonProp.min = globalMin;
    } else if (jsonProp.min < globalMin) {
      jsonProp.min = globalMin;
    }

    var globalMax = this.getMaxForType(jsonProp.type);
    if (jsonProp.max === undefined || jsonProp.max === null) {
      jsonProp.max = globalMax;
    } else if (jsonProp.max > globalMax) {
      jsonProp.min = globalMax;
    }

    if (!jsonProp.fillOpacity) {
      jsonProp.fillOpacity = false;
    }

    if ('number' != typeof jsonProp.lightness) {
      jsonProp.lightness = 1;
    } else {
      jsonProp.lightness = Math.max(0, Math.min(1, jsonProp.lightness));
    }

    if (!jsonProp.strokeOpacity) {
      jsonProp.strokeOpacity = false;
    }

    if (jsonProp.length === undefined || jsonProp.length === null) {
      jsonProp.length = Number.MAX_VALUE;
    }

    if (!jsonProp.wrapLines) {
      jsonProp.wrapLines = false;
    }

    if (!jsonProp.dateFormat) {
      jsonProp.dateFormat = ORYX.I18N.PropertyWindow.dateFormat || 'm/d/y';
    }

    if (!jsonProp.fill) {
      jsonProp.fill = false;
    }

    if (!jsonProp.stroke) {
      jsonProp.stroke = false;
    }

    if (!jsonProp.inverseBoolean) {
      jsonProp.inverseBoolean = false;
    }

    if (!jsonProp.directlyEditable && jsonProp.directlyEditable != false) {
      jsonProp.directlyEditable = true;
    }

    if (jsonProp.visible !== false) {
      jsonProp.visible = true;
    }

    if (jsonProp.isList !== true) {
      jsonProp.isList = false;

      if (!jsonProp.list || !(jsonProp.list instanceof Array)) {
        jsonProp.list = [];
      }
    }

    if (!jsonProp.category) {
      if (jsonProp.popular) {
        jsonProp.category = 'popular';
      } else {
        jsonProp.category = 'others';
      }
    }

    if (!jsonProp.alwaysAppearInMultiselect) {
      jsonProp.alwaysAppearInMultiselect = false;
    }

    if (jsonProp.type === ORYX.CONFIG.TYPE_CHOICE) {
      if (jsonProp.items && jsonProp.items instanceof Array) {
        jsonProp.items.each(
          function (jsonItem) {
            // why is the item's value used as the key???
            this._items[jsonItem.value.toLowerCase()] = new ORYX.Core.StencilSet.PropertyItem(jsonItem, namespace, this);
          }.bind(this)
        );
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.';
      }
      // extended by Kerstin (start)
    } else if (jsonProp.type === ORYX.CONFIG.TYPE_COMPLEX || jsonProp.type == ORYX.CONFIG.TYPE_MULTIPLECOMPLEX) {
      if (jsonProp.complexItems && jsonProp.complexItems instanceof Array) {
        jsonProp.complexItems.each(
          function (jsonComplexItem) {
            this._complexItems[jsonComplexItem.id.toLowerCase()] = new ORYX.Core.StencilSet.ComplexPropertyItem(
              jsonComplexItem,
              namespace,
              this
            );
          }.bind(this)
        );
      }
    }
    // extended by Kerstin (end)
  },

  getMinForType: function (type) {
    if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) {
      return -Math.pow(2, 31);
    } else {
      return -Number.MAX_VALUE + 1;
    }
  },
  getMaxForType: function (type) {
    if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) {
      return Math.pow(2, 31) - 1;
    } else {
      return Number.MAX_VALUE;
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.Property} property
   * @return {Boolean} True, if property has the same namespace and id.
   */
  equals: function (property) {
    return this._namespace === property.namespace() && this.id() === property.id() ? true : false;
  },

  namespace: function () {
    return this._namespace;
  },

  stencil: function () {
    return this._stencil;
  },

  id: function () {
    return this._jsonProp.id;
  },

  prefix: function () {
    return this._jsonProp.prefix;
  },

  type: function () {
    return this._jsonProp.type;
  },

  inverseBoolean: function () {
    return this._jsonProp.inverseBoolean;
  },

  category: function () {
    return this._jsonProp.category;
  },

  setCategory: function (value) {
    this._jsonProp.category = value;
  },

  directlyEditable: function () {
    return this._jsonProp.directlyEditable;
  },

  visible: function () {
    return this._jsonProp.visible;
  },

  title: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonProp, 'title');
  },

  value: function () {
    return this._jsonProp.value;
  },

  readonly: function () {
    return this._jsonProp.readonly;
  },

  optional: function () {
    return this._jsonProp.optional;
  },

  description: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonProp, 'description');
  },

  /**
   * An optional link to a SVG element so that the property affects the
   * graphical representation of the stencil.
   */
  refToView: function () {
    return this._jsonProp.refToView;
  },

  /**
   * If type is integer or float, min is the lower bounds of value.
   */
  min: function () {
    return this._jsonProp.min;
  },

  /**
   * If type ist integer or float, max is the upper bounds of value.
   */
  max: function () {
    return this._jsonProp.max;
  },

  /**
   * If type is float, this method returns if the fill-opacity property should
   *  be set.
   *  @return {Boolean}
   */
  fillOpacity: function () {
    return this._jsonProp.fillOpacity;
  },

  /**
   * If type is float, this method returns if the stroke-opacity property should
   *  be set.
   *  @return {Boolean}
   */
  strokeOpacity: function () {
    return this._jsonProp.strokeOpacity;
  },

  /**
   * If type is string or richtext, length is the maximum length of the text.
   * TODO how long can a string be.
   */
  length: function () {
    return this._jsonProp.length ? this._jsonProp.length : Number.MAX_VALUE;
  },

  wrapLines: function () {
    return this._jsonProp.wrapLines;
  },

  /**
   * If type is date, dateFormat specifies the format of the date. The format
   * specification of the ext library is used:
   *
   * Format  Output      Description
   *	------  ----------  --------------------------------------------------------------
   *	  d      10         Day of the month, 2 digits with leading zeros
   *	  D      Wed        A textual representation of a day, three letters
   *	  j      10         Day of the month without leading zeros
   *	  l      Wednesday  A full textual representation of the day of the week
   *	  S      th         English ordinal day of month suffix, 2 chars (use with j)
   *	  w      3          Numeric representation of the day of the week
   *	  z      9          The julian date, or day of the year (0-365)
   *	  W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
   *	  F      January    A full textual representation of the month
   *	  m      01         Numeric representation of a month, with leading zeros
   *	  M      Jan        Month name abbreviation, three letters
   *	  n      1          Numeric representation of a month, without leading zeros
   *	  t      31         Number of days in the given month
   *	  L      0          Whether its a leap year (1 if it is a leap year, else 0)
   *	  Y      2007       A full numeric representation of a year, 4 digits
   *	  y      07         A two digit representation of a year
   *	  a      pm         Lowercase Ante meridiem and Post meridiem
   *	  A      PM         Uppercase Ante meridiem and Post meridiem
   *	  g      3          12-hour format of an hour without leading zeros
   *	  G      15         24-hour format of an hour without leading zeros
   *	  h      03         12-hour format of an hour with leading zeros
   *	  H      15         24-hour format of an hour with leading zeros
   *	  i      05         Minutes with leading zeros
   *	  s      01         Seconds, with leading zeros
   *	  O      -0600      Difference to Greenwich time (GMT) in hours
   *	  T      CST        Timezone setting of the machine running the code
   *	  Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
   *
   * Example:
   *  F j, Y, g:i a  ->  January 10, 2007, 3:05 pm
   */
  dateFormat: function () {
    return this._jsonProp.dateFormat;
  },

  /**
   * If type is color, this method returns if the fill property should
   *  be set.
   *  @return {Boolean}
   */
  fill: function () {
    return this._jsonProp.fill;
  },

  /**
   * Lightness defines the satiation of the color
   * 0 is the pure color
   * 1 is white
   * @return {Integer} lightness
   */
  lightness: function () {
    return this._jsonProp.lightness;
  },

  /**
   * If type is color, this method returns if the stroke property should
   *  be set.
   *  @return {Boolean}
   */
  stroke: function () {
    return this._jsonProp.stroke;
  },

  /**
   * If type is choice, items is a hash map with all alternative values
   * (PropertyItem objects) with id as keys.
   */
  items: function () {
    return $H(this._items).values();
  },

  item: function (value) {
    if (value) {
      return this._items[value.toLowerCase()];
    } else {
      return null;
    }
  },

  toString: function () {
    return 'Property ' + this.title() + ' (' + this.id() + ')';
  },

  complexItems: function () {
    return $H(this._complexItems).values();
  },

  complexItem: function (id) {
    if (id) {
      return this._complexItems[id.toLowerCase()];
    } else {
      return null;
    }
  },

  complexAttributeToView: function () {
    return this._jsonProp.complexAttributeToView || '';
  },

  isList: function () {
    return !!this._jsonProp.isList;
  },

  getListItems: function () {
    return this._jsonProp.list;
  },

  /**
   * If type is glossary link, the
   * type of category can be defined where
   * the link only can go to.
   * @return {String} The glossary category id
   */
  linkableType: function () {
    return this._jsonProp.linkableType || '';
  },

  alwaysAppearInMultiselect: function () {
    return this._jsonProp.alwaysAppearInMultiselect;
  },

  popular: function () {
    return this._jsonProp.popular || false;
  },

  setPopular: function () {
    this._jsonProp.popular = true;
  },

  hide: function () {
    this._hidden = true;
  },

  isHidden: function () {
    return this._hidden;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.PropertyItem = Clazz.extend({
  /**
   * Constructor
   */
  construct: function (jsonItem, namespace, property) {
    arguments.callee.$.construct.apply(this, arguments);

    if (!jsonItem) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter jsonItem is not defined.';
    }
    if (!namespace) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter namespace is not defined.';
    }
    if (!property) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Parameter property is not defined.';
    }

    this._jsonItem = jsonItem;
    this._namespace = namespace;
    this._property = property;

    //init all values
    if (!jsonItem.value) {
      throw 'ORYX.Core.StencilSet.PropertyItem(construct): Value is not defined.';
    }

    if (this._jsonItem.refToView) {
      if (!(this._jsonItem.refToView instanceof Array)) {
        this._jsonItem.refToView = [this._jsonItem.refToView];
      }
    } else {
      this._jsonItem.refToView = [];
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.PropertyItem} item
   * @return {Boolean} True, if item has the same namespace and id.
   */
  equals: function (item) {
    return this.property().equals(item.property()) && this.value() === item.value();
  },

  namespace: function () {
    return this._namespace;
  },

  property: function () {
    return this._property;
  },

  value: function () {
    return this._jsonItem.value;
  },

  title: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonItem, 'title');
  },

  refToView: function () {
    return this._jsonItem.refToView;
  },

  icon: function () {
    return this._jsonItem.icon ? this.property().stencil()._source + 'icons/' + this._jsonItem.icon : '';
  },

  toString: function () {
    return 'PropertyItem ' + this.property() + ' (' + this.value() + ')';
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class Stencil
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.ComplexPropertyItem = Clazz.extend({
  /**
   * Constructor
   */
  construct: function (jsonItem, namespace, property) {
    arguments.callee.$.construct.apply(this, arguments);

    if (!jsonItem) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter jsonItem is not defined.';
    }
    if (!namespace) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter namespace is not defined.';
    }
    if (!property) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter property is not defined.';
    }

    this._jsonItem = jsonItem;
    this._namespace = namespace;
    this._property = property;
    this._items = new Hash();
    this._complexItems = new Hash();

    //init all values
    if (!jsonItem.name) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Name is not defined.';
    }

    if (!jsonItem.type) {
      throw 'ORYX.Core.StencilSet.ComplexPropertyItem(construct): Type is not defined.';
    } else {
      jsonItem.type = jsonItem.type.toLowerCase();
    }

    if (jsonItem.type === ORYX.CONFIG.TYPE_CHOICE) {
      if (jsonItem.items && jsonItem.items instanceof Array) {
        jsonItem.items.each(
          function (item) {
            this._items[item.value] = new ORYX.Core.StencilSet.PropertyItem(item, namespace, this);
          }.bind(this)
        );
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.';
      }
    } else if (jsonItem.type === ORYX.CONFIG.TYPE_COMPLEX) {
      if (jsonItem.complexItems && jsonItem.complexItems instanceof Array) {
        jsonItem.complexItems.each(
          function (complexItem) {
            this._complexItems[complexItem.id] = new ORYX.Core.StencilSet.ComplexPropertyItem(complexItem, namespace, this);
          }.bind(this)
        );
      } else {
        throw 'ORYX.Core.StencilSet.Property(construct): No property items defined.';
      }
    }
  },

  /**
   * @param {ORYX.Core.StencilSet.PropertyItem} item
   * @return {Boolean} True, if item has the same namespace and id.
   */
  equals: function (item) {
    return this.property().equals(item.property()) && this.name() === item.name();
  },

  namespace: function () {
    return this._namespace;
  },

  property: function () {
    return this._property;
  },

  name: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonItem, 'name');
  },

  id: function () {
    return this._jsonItem.id;
  },

  type: function () {
    return this._jsonItem.type;
  },

  optional: function () {
    return this._jsonItem.optional;
  },

  width: function () {
    return this._jsonItem.width;
  },

  value: function () {
    return this._jsonItem.value;
  },

  items: function () {
    return this._items.values();
  },

  complexItems: function () {
    return this._complexItems.values();
  },

  disable: function () {
    return this._jsonItem.disable;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class Rules uses Prototpye 1.5.0 uses Inheritance
 *
 * This class implements the API to check the stencil sets' rules.
 */
ORYX.Core.StencilSet.Rules = {
  /**
   * Constructor
   */
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    this._stencilSets = [];
    this._stencils = [];
    this._containerStencils = [];

    this._cachedConnectSET = new Hash();
    this._cachedConnectSE = new Hash();
    this._cachedConnectTE = new Hash();
    this._cachedCardSE = new Hash();
    this._cachedCardTE = new Hash();
    this._cachedContainPC = new Hash();
    this._cachedMorphRS = new Hash();

    this._connectionRules = new Hash();
    this._cardinalityRules = new Hash();
    this._containmentRules = new Hash();
    this._morphingRules = new Hash();
    this._layoutRules = new Hash();
  },

  /**
   * Call this method to initialize the rules for a stencil set and all of its
   * active extensions.
   *
   * @param {Object}
   *            stencilSet
   */
  initializeRules: function (stencilSet) {
    var existingSS = this._stencilSets.find(function (ss) {
      return ss.namespace() == stencilSet.namespace();
    });
    if (existingSS) {
      // reinitialize all rules
      var stencilsets = this._stencilSets.clone();
      stencilsets = stencilsets.without(existingSS);
      stencilsets.push(stencilSet);

      this._stencilSets = [];
      this._stencils = [];
      this._containerStencils = [];

      this._cachedConnectSET = new Hash();
      this._cachedConnectSE = new Hash();
      this._cachedConnectTE = new Hash();
      this._cachedCardSE = new Hash();
      this._cachedCardTE = new Hash();
      this._cachedContainPC = new Hash();
      this._cachedMorphRS = new Hash();

      this._connectionRules = new Hash();
      this._cardinalityRules = new Hash();
      this._containmentRules = new Hash();
      this._morphingRules = new Hash();
      this._layoutRules = new Hash();

      stencilsets.each(
        function (ss) {
          this.initializeRules(ss);
        }.bind(this)
      );
      return;
    } else {
      this._stencilSets.push(stencilSet);

      var jsonRules = new Hash(stencilSet.jsonRules());
      var namespace = stencilSet.namespace();
      var stencils = stencilSet.stencils();

      stencilSet
        .extensions()
        .values()
        .each(function (extension) {
          if (extension.rules) {
            if (extension.rules.connectionRules)
              jsonRules.connectionRules = jsonRules.connectionRules.concat(extension.rules.connectionRules);
            if (extension.rules.cardinalityRules)
              jsonRules.cardinalityRules = jsonRules.cardinalityRules.concat(extension.rules.cardinalityRules);
            if (extension.rules.containmentRules)
              jsonRules.containmentRules = jsonRules.containmentRules.concat(extension.rules.containmentRules);
            if (extension.rules.morphingRules) jsonRules.morphingRules = jsonRules.morphingRules.concat(extension.rules.morphingRules);
          }
          if (extension.stencils) stencils = stencils.concat(extension.stencils);
        });

      this._stencils = this._stencils.concat(stencilSet.stencils());

      // init connection rules
      var cr = this._connectionRules;
      if (jsonRules.connectionRules) {
        jsonRules.connectionRules.each(
          function (rules) {
            if (this._isRoleOfOtherNamespace(rules.role)) {
              if (!cr[rules.role]) {
                cr[rules.role] = new Hash();
              }
            } else {
              if (!cr[namespace + rules.role]) cr[namespace + rules.role] = new Hash();
            }

            rules.connects.each(
              function (connect) {
                var toRoles = [];
                if (connect.to) {
                  if (!(connect.to instanceof Array)) {
                    connect.to = [connect.to];
                  }
                  connect.to.each(
                    function (to) {
                      if (this._isRoleOfOtherNamespace(to)) {
                        toRoles.push(to);
                      } else {
                        toRoles.push(namespace + to);
                      }
                    }.bind(this)
                  );
                }

                var role, from;
                if (this._isRoleOfOtherNamespace(rules.role)) role = rules.role;
                else role = namespace + rules.role;

                if (this._isRoleOfOtherNamespace(connect.from)) from = connect.from;
                else from = namespace + connect.from;

                if (!cr[role][from]) cr[role][from] = toRoles;
                else cr[role][from] = cr[role][from].concat(toRoles);
              }.bind(this)
            );
          }.bind(this)
        );
      }

      // init cardinality rules
      var cardr = this._cardinalityRules;
      if (jsonRules.cardinalityRules) {
        jsonRules.cardinalityRules.each(
          function (rules) {
            var cardrKey;
            if (this._isRoleOfOtherNamespace(rules.role)) {
              cardrKey = rules.role;
            } else {
              cardrKey = namespace + rules.role;
            }

            if (!cardr[cardrKey]) {
              cardr[cardrKey] = {};
              for (i in rules) {
                cardr[cardrKey][i] = rules[i];
              }
            }

            var oe = new Hash();
            if (rules.outgoingEdges) {
              rules.outgoingEdges.each(
                function (rule) {
                  if (this._isRoleOfOtherNamespace(rule.role)) {
                    oe[rule.role] = rule;
                  } else {
                    oe[namespace + rule.role] = rule;
                  }
                }.bind(this)
              );
            }
            cardr[cardrKey].outgoingEdges = oe;
            var ie = new Hash();
            if (rules.incomingEdges) {
              rules.incomingEdges.each(
                function (rule) {
                  if (this._isRoleOfOtherNamespace(rule.role)) {
                    ie[rule.role] = rule;
                  } else {
                    ie[namespace + rule.role] = rule;
                  }
                }.bind(this)
              );
            }
            cardr[cardrKey].incomingEdges = ie;
          }.bind(this)
        );
      }

      // init containment rules
      var conr = this._containmentRules;
      if (jsonRules.containmentRules) {
        jsonRules.containmentRules.each(
          function (rules) {
            var conrKey;
            if (this._isRoleOfOtherNamespace(rules.role)) {
              conrKey = rules.role;
            } else {
              this._containerStencils.push(namespace + rules.role);
              conrKey = namespace + rules.role;
            }
            if (!conr[conrKey]) {
              conr[conrKey] = [];
            }
            (rules.contains || []).each(
              function (containRole) {
                if (this._isRoleOfOtherNamespace(containRole)) {
                  conr[conrKey].push(containRole);
                } else {
                  conr[conrKey].push(namespace + containRole);
                }
              }.bind(this)
            );
          }.bind(this)
        );
      }

      // init morphing rules
      var morphr = this._morphingRules;
      if (jsonRules.morphingRules) {
        jsonRules.morphingRules.each(
          function (rules) {
            var morphrKey;
            if (this._isRoleOfOtherNamespace(rules.role)) {
              morphrKey = rules.role;
            } else {
              morphrKey = namespace + rules.role;
            }
            if (!morphr[morphrKey]) {
              morphr[morphrKey] = [];
            }
            if (!rules.preserveBounds) {
              rules.preserveBounds = false;
            }
            rules.baseMorphs.each(
              function (baseMorphStencilId) {
                var morphStencil = this._getStencilById(namespace + baseMorphStencilId);
                if (morphStencil) {
                  morphr[morphrKey].push(morphStencil);
                }
              }.bind(this)
            );
          }.bind(this)
        );
      }

      // init layouting rules
      var layoutRules = this._layoutRules;
      if (jsonRules.layoutRules) {
        var getDirections = function (o) {
          return {
            edgeRole: o.edgeRole || undefined,
            t: o['t'] || 1,
            r: o['r'] || 1,
            b: o['b'] || 1,
            l: o['l'] || 1,
          };
        };

        jsonRules.layoutRules.each(
          function (rules) {
            var layoutKey;
            if (this._isRoleOfOtherNamespace(rules.role)) {
              layoutKey = rules.role;
            } else {
              layoutKey = namespace + rules.role;
            }
            if (!layoutRules[layoutKey]) {
              layoutRules[layoutKey] = {};
            }
            if (rules['in']) {
              layoutRules[layoutKey]['in'] = getDirections(rules['in']);
            }
            if (rules['ins']) {
              layoutRules[layoutKey]['ins'] = (rules['ins'] || []).map(function (e) {
                return getDirections(e);
              });
            }
            if (rules['out']) {
              layoutRules[layoutKey]['out'] = getDirections(rules['out']);
            }
            if (rules['outs']) {
              layoutRules[layoutKey]['outs'] = (rules['outs'] || []).map(function (e) {
                return getDirections(e);
              });
            }
          }.bind(this)
        );
      }
    }
  },

  _getStencilById: function (id) {
    return this._stencils.find(function (stencil) {
      return stencil.id() == id;
    });
  },

  _cacheConnect: function (args) {
    result = this._canConnect(args);

    if (args.sourceStencil && args.targetStencil) {
      var source = this._cachedConnectSET[args.sourceStencil.id()];

      if (!source) {
        source = new Hash();
        this._cachedConnectSET[args.sourceStencil.id()] = source;
      }

      var edge = source[args.edgeStencil.id()];

      if (!edge) {
        edge = new Hash();
        source[args.edgeStencil.id()] = edge;
      }

      edge[args.targetStencil.id()] = result;
    } else if (args.sourceStencil) {
      var source = this._cachedConnectSE[args.sourceStencil.id()];

      if (!source) {
        source = new Hash();
        this._cachedConnectSE[args.sourceStencil.id()] = source;
      }

      source[args.edgeStencil.id()] = result;
    } else {
      var target = this._cachedConnectTE[args.targetStencil.id()];

      if (!target) {
        target = new Hash();
        this._cachedConnectTE[args.targetStencil.id()] = target;
      }

      target[args.edgeStencil.id()] = result;
    }

    return result;
  },

  _cacheCard: function (args) {
    if (args.sourceStencil) {
      var source = this._cachedCardSE[args.sourceStencil.id()];

      if (!source) {
        source = new Hash();
        this._cachedCardSE[args.sourceStencil.id()] = source;
      }

      var max = this._getMaximumNumberOfOutgoingEdge(args);
      if (max == undefined) max = -1;

      source[args.edgeStencil.id()] = max;
    }

    if (args.targetStencil) {
      var target = this._cachedCardTE[args.targetStencil.id()];

      if (!target) {
        target = new Hash();
        this._cachedCardTE[args.targetStencil.id()] = target;
      }

      var max = this._getMaximumNumberOfIncomingEdge(args);
      if (max == undefined) max = -1;

      target[args.edgeStencil.id()] = max;
    }
  },

  _cacheContain: function (args) {
    var result = [this._canContain(args), this._getMaximumOccurrence(args.containingStencil, args.containedStencil)];

    if (result[1] == undefined) result[1] = -1;

    var children = this._cachedContainPC[args.containingStencil.id()];

    if (!children) {
      children = new Hash();
      this._cachedContainPC[args.containingStencil.id()] = children;
    }

    children[args.containedStencil.id()] = result;

    return result;
  },

  /**
   * Returns all stencils belonging to a morph group. (calculation result is
   * cached)
   */
  _cacheMorph: function (role) {
    var morphs = this._cachedMorphRS[role];

    if (!morphs) {
      morphs = [];

      if (this._morphingRules.keys().include(role)) {
        morphs = this._stencils.select(function (stencil) {
          return stencil.roles().include(role);
        });
      }

      this._cachedMorphRS[role] = morphs;
    }
    return morphs;
  },

  /** Begin connection rules' methods */

  /**
   *
   * @param {Object}
   *            args sourceStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            sourceShape: ORYX.Core.Shape | undefined
   *
   * At least sourceStencil or sourceShape has to be specified
   *
   * @return {Array} Array of stencils of edges that can be outgoing edges of
   *         the source.
   */
  outgoingEdgeStencils: function (args) {
    // check arguments
    if (!args.sourceShape && !args.sourceStencil) {
      return [];
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil();
    }

    var _edges = [];

    // test each edge, if it can connect to source
    this._stencils.each(
      function (stencil) {
        if (stencil.type() === 'edge') {
          var newArgs = Object.clone(args);
          newArgs.edgeStencil = stencil;
          if (this.canConnect(newArgs)) {
            _edges.push(stencil);
          }
        }
      }.bind(this)
    );

    return _edges;
  },

  /**
   *
   * @param {Object}
   *            args targetStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            targetShape: ORYX.Core.Shape | undefined
   *
   * At least targetStencil or targetShape has to be specified
   *
   * @return {Array} Array of stencils of edges that can be incoming edges of
   *         the target.
   */
  incomingEdgeStencils: function (args) {
    // check arguments
    if (!args.targetShape && !args.targetStencil) {
      return [];
    }

    // init arguments
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil();
    }

    var _edges = [];

    // test each edge, if it can connect to source
    this._stencils.each(
      function (stencil) {
        if (stencil.type() === 'edge') {
          var newArgs = Object.clone(args);
          newArgs.edgeStencil = stencil;
          if (this.canConnect(newArgs)) {
            _edges.push(stencil);
          }
        }
      }.bind(this)
    );

    return _edges;
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            edgeShape: ORYX.Core.Edge | undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node | undefined
   *
   * At least edgeStencil or edgeShape has to be specified!!!
   *
   * @return {Array} Returns an array of stencils that can be source of the
   *         specified edge.
   */
  sourceStencils: function (args) {
    // check arguments
    if (!args || (!args.edgeShape && !args.edgeStencil)) {
      return [];
    }

    // init arguments
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil();
    }

    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil();
    }

    var _sources = [];

    // check each stencil, if it can be a source
    this._stencils.each(
      function (stencil) {
        var newArgs = Object.clone(args);
        newArgs.sourceStencil = stencil;
        if (this.canConnect(newArgs)) {
          _sources.push(stencil);
        }
      }.bind(this)
    );

    return _sources;
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined
   *            edgeShape: ORYX.Core.Edge | undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node | undefined
   *
   * At least edgeStencil or edgeShape has to be specified!!!
   *
   * @return {Array} Returns an array of stencils that can be target of the
   *         specified edge.
   */
  targetStencils: function (args) {
    // check arguments
    if (!args || (!args.edgeShape && !args.edgeStencil)) {
      return [];
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil();
    }

    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil();
    }

    var _targets = [];

    // check stencil, if it can be a target
    this._stencils.each(
      function (stencil) {
        var newArgs = Object.clone(args);
        newArgs.targetStencil = stencil;
        if (this.canConnect(newArgs)) {
          _targets.push(stencil);
        }
      }.bind(this)
    );

    return _targets;
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape:
   *            ORYX.Core.Edge |undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node |undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node |undefined
   *
   * At least source or target has to be specified!!!
   *
   * @return {Boolean} Returns, if the edge can connect source and target.
   */
  canConnect: function (args) {
    // check arguments
    if (
      !args ||
      (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil) ||
      (!args.edgeShape && !args.edgeStencil)
    ) {
      return false;
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil();
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil();
    }
    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil();
    }

    var result;

    if (args.sourceStencil && args.targetStencil) {
      var source = this._cachedConnectSET[args.sourceStencil.id()];

      if (!source) result = this._cacheConnect(args);
      else {
        var edge = source[args.edgeStencil.id()];

        if (!edge) result = this._cacheConnect(args);
        else {
          var target = edge[args.targetStencil.id()];

          if (target == undefined) result = this._cacheConnect(args);
          else result = target;
        }
      }
    } else if (args.sourceStencil) {
      var source = this._cachedConnectSE[args.sourceStencil.id()];

      if (!source) result = this._cacheConnect(args);
      else {
        var edge = source[args.edgeStencil.id()];

        if (edge == undefined) result = this._cacheConnect(args);
        else result = edge;
      }
    } else {
      // args.targetStencil
      var target = this._cachedConnectTE[args.targetStencil.id()];

      if (!target) result = this._cacheConnect(args);
      else {
        var edge = target[args.edgeStencil.id()];

        if (edge == undefined) result = this._cacheConnect(args);
        else result = edge;
      }
    }

    // check cardinality
    if (result) {
      if (args.sourceShape) {
        var source = this._cachedCardSE[args.sourceStencil.id()];

        if (!source) {
          this._cacheCard(args);
          source = this._cachedCardSE[args.sourceStencil.id()];
        }

        var max = source[args.edgeStencil.id()];

        if (max == undefined) {
          this._cacheCard(args);
        }

        max = source[args.edgeStencil.id()];

        if (max != -1) {
          result = args.sourceShape.getOutgoingShapes().all(function (cs) {
            if (cs.getStencil().id() === args.edgeStencil.id() && (args.edgeShape ? cs !== args.edgeShape : true)) {
              max--;
              return max > 0 ? true : false;
            } else {
              return true;
            }
          });
        }
      }

      if (args.targetShape) {
        var target = this._cachedCardTE[args.targetStencil.id()];

        if (!target) {
          this._cacheCard(args);
          target = this._cachedCardTE[args.targetStencil.id()];
        }

        var max = target[args.edgeStencil.id()];

        if (max == undefined) {
          this._cacheCard(args);
        }

        max = target[args.edgeStencil.id()];

        if (max != -1) {
          result = args.targetShape.getIncomingShapes().all(function (cs) {
            if (cs.getStencil().id() === args.edgeStencil.id() && (args.edgeShape ? cs !== args.edgeShape : true)) {
              max--;
              return max > 0 ? true : false;
            } else {
              return true;
            }
          });
        }
      }
    }

    return result;
  },

  /**
   *
   * @param {Object}
   *            args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape:
   *            ORYX.Core.Edge |undefined sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined sourceShape:
   *            ORYX.Core.Node |undefined targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined targetShape:
   *            ORYX.Core.Node |undefined
   *
   * At least source or target has to be specified!!!
   *
   * @return {Boolean} Returns, if the edge can connect source and target.
   */
  _canConnect: function (args) {
    // check arguments
    if (
      !args ||
      (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil) ||
      (!args.edgeShape && !args.edgeStencil)
    ) {
      return false;
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil();
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil();
    }
    if (args.edgeShape) {
      args.edgeStencil = args.edgeShape.getStencil();
    }

    // 1. check connection rules
    var resultCR;

    // get all connection rules for this edge
    var edgeRules = this._getConnectionRulesOfEdgeStencil(args.edgeStencil);

    // check connection rules, if the source can be connected to the target
    // with the specified edge.
    if (edgeRules.keys().length === 0) {
      resultCR = false;
    } else {
      if (args.sourceStencil) {
        resultCR = args.sourceStencil.roles().any(function (sourceRole) {
          var targetRoles = edgeRules[sourceRole];

          if (!targetRoles) {
            return false;
          }

          if (args.targetStencil) {
            return targetRoles.any(function (targetRole) {
              return args.targetStencil.roles().member(targetRole);
            });
          } else {
            return true;
          }
        });
      } else {
        // !args.sourceStencil -> there is args.targetStencil
        resultCR = edgeRules.values().any(function (targetRoles) {
          return args.targetStencil.roles().any(function (targetRole) {
            return targetRoles.member(targetRole);
          });
        });
      }
    }

    return resultCR;
  },

  /** End connection rules' methods */

  /** Begin containment rules' methods */

  isContainer: function (shape) {
    return this._containerStencils.member(shape.getStencil().id());
  },

  /**
   *
   * @param {Object}
   *            args containingStencil: ORYX.Core.StencilSet.Stencil
   *            containingShape: ORYX.Core.AbstractShape containedStencil:
   *            ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape
   */
  canContain: function (args) {
    if (!args || (!args.containingStencil && !args.containingShape) || (!args.containedStencil && !args.containedShape)) {
      return false;
    }

    // init arguments
    if (args.containedShape) {
      args.containedStencil = args.containedShape.getStencil();
    }

    if (args.containingShape) {
      args.containingStencil = args.containingShape.getStencil();
    }

    //if(args.containingStencil.type() == 'edge' || args.containedStencil.type() == 'edge')
    //	return false;
    if (args.containedStencil.type() == 'edge') return false;

    var childValues;

    var parent = this._cachedContainPC[args.containingStencil.id()];

    if (!parent) childValues = this._cacheContain(args);
    else {
      childValues = parent[args.containedStencil.id()];

      if (!childValues) childValues = this._cacheContain(args);
    }

    if (!childValues[0]) return false;
    else if (childValues[1] == -1) return true;
    else {
      if (args.containingShape) {
        var max = childValues[1];
        return args.containingShape.getChildShapes(false).all(function (as) {
          if (as.getStencil().id() === args.containedStencil.id()) {
            max--;
            return max > 0 ? true : false;
          } else {
            return true;
          }
        });
      } else {
        return true;
      }
    }
  },

  /**
   *
   * @param {Object}
   *            args containingStencil: ORYX.Core.StencilSet.Stencil
   *            containingShape: ORYX.Core.AbstractShape containedStencil:
   *            ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape
   */
  _canContain: function (args) {
    if (!args || (!args.containingStencil && !args.containingShape) || (!args.containedStencil && !args.containedShape)) {
      return false;
    }

    // init arguments
    if (args.containedShape) {
      args.containedStencil = args.containedShape.getStencil();
    }

    if (args.containingShape) {
      args.containingStencil = args.containingShape.getStencil();
    }

    //		if(args.containingShape) {
    //			if(args.containingShape instanceof ORYX.Core.Edge) {
    //				// edges cannot contain other shapes
    //				return false;
    //			}
    //		}

    var result;

    // check containment rules
    result = args.containingStencil.roles().any(
      function (role) {
        var roles = this._containmentRules[role];
        if (roles) {
          return roles.any(function (role) {
            return args.containedStencil.roles().member(role);
          });
        } else {
          return false;
        }
      }.bind(this)
    );

    return result;
  },

  /** End containment rules' methods */

  /** Begin morphing rules' methods */

  /**
   *
   * @param {Object}
   *           args
   *            stencil: ORYX.Core.StencilSet.Stencil | undefined
   *            shape: ORYX.Core.Shape | undefined
   *
   * At least stencil or shape has to be specified
   *
   * @return {Array} Array of stencils that the passed stencil/shape can be
   *         transformed to (including the current stencil itself)
   */
  morphStencils: function (args) {
    // check arguments
    if (!args.stencil && !args.shape) {
      return [];
    }

    // init arguments
    if (args.shape) {
      args.stencil = args.shape.getStencil();
    }

    var _morphStencils = [];
    args.stencil.roles().each(
      function (role) {
        this._cacheMorph(role).each(function (stencil) {
          _morphStencils.push(stencil);
        });
      }.bind(this)
    );

    var baseMorphs = this.baseMorphs();
    // BaseMorphs should be in the front of the array
    _morphStencils = _morphStencils.uniq().sort(function (a, b) {
      return baseMorphs.include(a) && !baseMorphs.include(b) ? -1 : baseMorphs.include(b) && !baseMorphs.include(a) ? 1 : 0;
    });
    return _morphStencils;
  },

  /**
   * @return {Array} An array of all base morph stencils
   */
  baseMorphs: function () {
    var _baseMorphs = [];
    this._morphingRules.each(function (pair) {
      pair.value.each(function (baseMorph) {
        _baseMorphs.push(baseMorph);
      });
    });
    return _baseMorphs;
  },

  /**
   * Returns true if there are morphing rules defines
   * @return {boolean}
   */
  containsMorphingRules: function () {
    return this._stencilSets.any(function (ss) {
      return !!ss.jsonRules().morphingRules;
    });
  },

  /**
   *
   * @param {Object}
   *            args
   *            sourceStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined
   *            sourceShape:
   *            ORYX.Core.Node |undefined
   *            targetStencil:
   *            ORYX.Core.StencilSet.Stencil | undefined
   *            targetShape:
   *            ORYX.Core.Node |undefined
   *
   *
   * @return {Stencil} Returns, the stencil for the connecting edge
   * or null if connection is not possible
   */
  connectMorph: function (args) {
    // check arguments
    if (!args || (!args.sourceShape && !args.sourceStencil && !args.targetShape && !args.targetStencil)) {
      return false;
    }

    // init arguments
    if (args.sourceShape) {
      args.sourceStencil = args.sourceShape.getStencil();
    }
    if (args.targetShape) {
      args.targetStencil = args.targetShape.getStencil();
    }

    var incoming = this.incomingEdgeStencils(args);
    var outgoing = this.outgoingEdgeStencils(args);

    var edgeStencils = incoming.select(function (e) {
      return outgoing.member(e);
    }); // intersection of sets
    var baseEdgeStencils = this.baseMorphs().select(function (e) {
      return edgeStencils.member(e);
    }); // again: intersection of sets

    if (baseEdgeStencils.size() > 0) return baseEdgeStencils[0];
    // return any of the possible base morphs
    else if (edgeStencils.size() > 0) return edgeStencils[0]; // return any of the possible stencils

    return null; //connection not possible
  },

  /**
   * Return true if the stencil should be located in the shape menu
   * @param {ORYX.Core.StencilSet.Stencil} morph
   * @return {Boolean} Returns true if the morphs in the morph group of the
   * specified morph shall be displayed in the shape menu
   */
  showInShapeMenu: function (stencil) {
    return this._stencilSets.any(function (ss) {
      return ss.jsonRules().morphingRules.any(function (r) {
        return stencil.roles().include(ss.namespace() + r.role) && r.showInShapeMenu !== false;
      });
    });
  },

  preserveBounds: function (stencil) {
    return this._stencilSets.any(function (ss) {
      return ss.jsonRules().morphingRules.any(function (r) {
        return stencil.roles().include(ss.namespace() + r.role) && r.preserveBounds;
      });
    });
  },

  /** End morphing rules' methods */

  /** Begin layouting rules' methods */

  /**
   * Returns a set on "in" and "out" layouting rules for a given shape
   * @param {Object} shape
   * @param {Object} edgeShape (Optional)
   * @return {Object} "in" and "out" with a default value of {"t":1, "r":1, "b":1, "r":1} if not specified in the json
   */
  getLayoutingRules: function (shape, edgeShape) {
    if (!shape || !(shape instanceof ORYX.Core.Shape)) {
      return;
    }

    var layout = { in: {}, out: {} };

    var parseValues = function (o, v) {
      if (o && o[v]) {
        ['t', 'r', 'b', 'l'].each(function (d) {
          layout[v][d] = Math.max(o[v][d], layout[v][d] || 0);
        });
      }
      if (o && o[v + 's'] instanceof Array) {
        ['t', 'r', 'b', 'l'].each(
          function (d) {
            var defaultRule = o[v + 's'].find(function (e) {
              return !e.edgeRole;
            });
            var edgeRule;
            if (edgeShape instanceof ORYX.Core.Edge) {
              edgeRule = o[v + 's'].find(
                function (e) {
                  return this._hasRole(edgeShape, e.edgeRole);
                }.bind(this)
              );
            }
            layout[v][d] = Math.max(edgeRule ? edgeRule[d] : defaultRule[d], layout[v][d] || 0);
          }.bind(this)
        );
      }
    }.bind(this);

    // For each role
    shape
      .getStencil()
      .roles()
      .each(
        function (role) {
          // check if there are layout information
          if (this._layoutRules[role]) {
            // if so, parse those information to the 'layout' variable
            parseValues(this._layoutRules[role], 'in');
            parseValues(this._layoutRules[role], 'out');
          }
        }.bind(this)
      );

    // Make sure, that every attribute has an value,
    // otherwise set 1
    ['in', 'out'].each(function (v) {
      ['t', 'r', 'b', 'l'].each(function (d) {
        layout[v][d] = layout[v][d] !== undefined ? layout[v][d] : 1;
      });
    });

    return layout;
  },

  /** End layouting rules' methods */

  /** Helper methods */

  /**
   * Checks wether a shape contains the given role or the role is equal the stencil id
   * @param {ORYX.Core.Shape} shape
   * @param {String} role
   */
  _hasRole: function (shape, role) {
    if (!(shape instanceof ORYX.Core.Shape) || !role) {
      return;
    }
    var isRole = shape
      .getStencil()
      .roles()
      .any(function (r) {
        return r == role;
      });

    return isRole || shape.getStencil().id() == shape.getStencil().namespace() + role;
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role.
   */
  _stencilsWithRole: function (role) {
    return this._stencils.findAll(function (stencil) {
      return stencil.roles().member(role) ? true : false;
    });
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role and
   *         have the type 'edge'.
   */
  _edgesWithRole: function (role) {
    return this._stencils.findAll(function (stencil) {
      return stencil.roles().member(role) && stencil.type() === 'edge' ? true : false;
    });
  },

  /**
   *
   * @param {String}
   *            role
   *
   * @return {Array} Returns an array of stencils that can act as role and
   *         have the type 'node'.
   */
  _nodesWithRole: function (role) {
    return this._stencils.findAll(function (stencil) {
      return stencil.roles().member(role) && stencil.type() === 'node' ? true : false;
    });
  },

  /**
   *
   * @param {ORYX.Core.StencilSet.Stencil}
   *            parent
   * @param {ORYX.Core.StencilSet.Stencil}
   *            child
   *
   * @returns {Boolean} Returns the maximum occurrence of shapes of the
   *          stencil's type inside the parent.
   */
  _getMaximumOccurrence: function (parent, child) {
    var max;
    child.roles().each(
      function (role) {
        var cardRule = this._cardinalityRules[role];
        if (cardRule && cardRule.maximumOccurrence) {
          if (max) {
            max = Math.min(max, cardRule.maximumOccurrence);
          } else {
            max = cardRule.maximumOccurrence;
          }
        }
      }.bind(this)
    );

    return max;
  },

  /**
   *
   * @param {Object}
   *            args sourceStencil: ORYX.Core.Node edgeStencil:
   *            ORYX.Core.StencilSet.Stencil
   *
   * @return {Boolean} Returns, the maximum number of outgoing edges of the
   *         type specified by edgeStencil of the sourceShape.
   */
  _getMaximumNumberOfOutgoingEdge: function (args) {
    if (!args || !args.sourceStencil || !args.edgeStencil) {
      return false;
    }

    var max;
    args.sourceStencil.roles().each(
      function (role) {
        var cardRule = this._cardinalityRules[role];

        if (cardRule && cardRule.outgoingEdges) {
          args.edgeStencil.roles().each(function (edgeRole) {
            var oe = cardRule.outgoingEdges[edgeRole];

            if (oe && oe.maximum) {
              if (max) {
                max = Math.min(max, oe.maximum);
              } else {
                max = oe.maximum;
              }
            }
          });
        }
      }.bind(this)
    );

    return max;
  },

  /**
   *
   * @param {Object}
   *            args targetStencil: ORYX.Core.StencilSet.Stencil edgeStencil:
   *            ORYX.Core.StencilSet.Stencil
   *
   * @return {Boolean} Returns the maximum number of incoming edges of the
   *         type specified by edgeStencil of the targetShape.
   */
  _getMaximumNumberOfIncomingEdge: function (args) {
    if (!args || !args.targetStencil || !args.edgeStencil) {
      return false;
    }

    var max;
    args.targetStencil.roles().each(
      function (role) {
        var cardRule = this._cardinalityRules[role];
        if (cardRule && cardRule.incomingEdges) {
          args.edgeStencil.roles().each(function (edgeRole) {
            var ie = cardRule.incomingEdges[edgeRole];
            if (ie && ie.maximum) {
              if (max) {
                max = Math.min(max, ie.maximum);
              } else {
                max = ie.maximum;
              }
            }
          });
        }
      }.bind(this)
    );

    return max;
  },

  /**
   *
   * @param {ORYX.Core.StencilSet.Stencil}
   *            edgeStencil
   *
   * @return {Hash} Returns a hash map of all connection rules for
   *         edgeStencil.
   */
  _getConnectionRulesOfEdgeStencil: function (edgeStencil) {
    var edgeRules = new Hash();
    edgeStencil.roles().each(
      function (role) {
        if (this._connectionRules[role]) {
          this._connectionRules[role].each(function (cr) {
            if (edgeRules[cr.key]) {
              edgeRules[cr.key] = edgeRules[cr.key].concat(cr.value);
            } else {
              edgeRules[cr.key] = cr.value;
            }
          });
        }
      }.bind(this)
    );

    return edgeRules;
  },

  _isRoleOfOtherNamespace: function (role) {
    return role.indexOf('#') > 0;
  },

  toString: function () {
    return 'Rules';
  },
};
ORYX.Core.StencilSet.Rules = Clazz.extend(ORYX.Core.StencilSet.Rules);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * This class represents a stencil set. It offers methods for accessing
 *  the attributes of the stencil set description JSON file and the stencil set's
 *  stencils.
 */
ORYX.Core.StencilSet.StencilSet = Clazz.extend({
  /**
   * Constructor
   * @param source {URL} A reference to the stencil set specification.
   *
   */
  construct: function (source, modelMetaData, editorId) {
    arguments.callee.$.construct.apply(this, arguments);

    if (!source) {
      throw "ORYX.Core.StencilSet.StencilSet(construct): Parameter 'source' is not defined.";
    }

    if (source.endsWith('/')) {
      source = source.substr(0, source.length - 1);
    }

    this._extensions = new Hash();

    this._source = source;
    this._baseUrl = source.substring(0, source.lastIndexOf('/') + 1);

    this._jsonObject = {};

    this._stencils = new Hash();
    this._availableStencils = new Hash();

    if (ORYX.CONFIG.BACKEND_SWITCH) {
      this._baseUrl = 'editor/stencilsets/bpmn2.0/';
      this._source = 'stencilsets/bpmn2.0/bpmn2.0.json';

      new Ajax.Request(ACTIVITI.CONFIG.contextRoot + '/editor/stencilset?version=' + Date.now(), {
        asynchronous: false,
        method: 'get',
        onSuccess: this._init.bind(this),
        onFailure: this._cancelInit.bind(this),
      });
    } else {
      new Ajax.Request(source, {
        asynchronous: false,
        method: 'get',
        onSuccess: this._init.bind(this),
        onFailure: this._cancelInit.bind(this),
      });
    }

    if (this.errornous) throw 'Loading stencil set ' + source + ' failed.';
  },

  /**
   * Finds a root stencil in this stencil set. There may be many of these. If
   * there are, the first one found will be used. In Firefox, this is the
   * topmost definition in the stencil set description file.
   */
  findRootStencilName: function () {
    // find any stencil that may be root.
    var rootStencil = this._stencils.values().find(function (stencil) {
      return stencil._jsonStencil.mayBeRoot;
    });

    // if there is none, just guess the first.
    if (!rootStencil) {
      ORYX.Log.warn('Did not find any stencil that may be root. Taking a guess.');
      rootStencil = this._stencils.values()[0];
    }

    // return its id.
    return rootStencil.id();
  },

  /**
   * @param {ORYX.Core.StencilSet.StencilSet} stencilSet
   * @return {Boolean} True, if stencil set has the same namespace.
   */
  equals: function (stencilSet) {
    return this.namespace() === stencilSet.namespace();
  },

  /**
   *
   * @param {Oryx.Core.StencilSet.Stencil} rootStencil If rootStencil is defined, it only returns stencils
   * 			that could be (in)direct child of that stencil.
   */
  stencils: function (rootStencil, rules, sortByGroup) {
    if (rootStencil && rules) {
      var stencils = this._availableStencils.values();
      var containers = [rootStencil];
      var checkedContainers = [];

      var result = [];

      while (containers.size() > 0) {
        var container = containers.pop();
        checkedContainers.push(container);
        var children = stencils.findAll(function (stencil) {
          var args = {
            containingStencil: container,
            containedStencil: stencil,
          };
          return rules.canContain(args);
        });
        for (var i = 0; i < children.size(); i++) {
          if (!checkedContainers.member(children[i])) {
            containers.push(children[i]);
          }
        }
        result = result.concat(children).uniq();
      }

      // Sort the result to the origin order
      result = result.sortBy(function (stencil) {
        return stencils.indexOf(stencil);
      });

      if (sortByGroup) {
        result = result.sortBy(function (stencil) {
          return stencil.groups().first();
        });
      }

      var edges = stencils.findAll(function (stencil) {
        return stencil.type() == 'edge';
      });
      result = result.concat(edges);

      return result;
    } else {
      if (sortByGroup) {
        return this._availableStencils.values().sortBy(function (stencil) {
          return stencil.groups().first();
        });
      } else {
        return this._availableStencils.values();
      }
    }
  },

  nodes: function () {
    return this._availableStencils.values().findAll(function (stencil) {
      return stencil.type() === 'node';
    });
  },

  edges: function () {
    return this._availableStencils.values().findAll(function (stencil) {
      return stencil.type() === 'edge';
    });
  },

  stencil: function (id) {
    return this._stencils[id];
  },

  title: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonObject, 'title');
  },

  description: function () {
    return ORYX.Core.StencilSet.getTranslation(this._jsonObject, 'description');
  },

  namespace: function () {
    return this._jsonObject ? this._jsonObject.namespace : null;
  },

  jsonRules: function () {
    return this._jsonObject ? this._jsonObject.rules : null;
  },

  source: function () {
    return this._source;
  },

  extensions: function () {
    return this._extensions;
  },

  addExtension: function (url) {
    new Ajax.Request(url, {
      method: 'GET',
      asynchronous: false,
      onSuccess: function (transport) {
        this.addExtensionDirectly(transport.responseText);
      }.bind(this),
      onFailure: function (transport) {
        ORYX.Log.debug('Loading stencil set extension file failed. The request returned an error.' + transport);
      }.bind(this),
      onException: function (transport) {
        ORYX.Log.debug('Loading stencil set extension file failed. The request returned an error.' + transport);
      }.bind(this),
    });
  },

  addExtensionDirectly: function (str) {
    try {
      eval('var jsonExtension = ' + str);

      if (!jsonExtension['extends'].endsWith('#')) jsonExtension['extends'] += '#';

      if (jsonExtension['extends'] == this.namespace()) {
        this._extensions[jsonExtension.namespace] = jsonExtension;

        var defaultPosition = this._stencils.keys().size();
        //load new stencils
        if (jsonExtension.stencils) {
          $A(jsonExtension.stencils).each(
            function (stencil) {
              defaultPosition++;
              var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this, undefined, defaultPosition);
              this._stencils[oStencil.id()] = oStencil;
              this._availableStencils[oStencil.id()] = oStencil;
            }.bind(this)
          );
        }

        //load additional properties
        if (jsonExtension.properties) {
          var stencils = this._stencils.values();

          stencils.each(
            function (stencil) {
              var roles = stencil.roles();

              jsonExtension.properties.each(function (prop) {
                prop.roles.any(function (role) {
                  role = jsonExtension['extends'] + role;
                  if (roles.member(role)) {
                    prop.properties.each(function (property) {
                      stencil.addProperty(property, jsonExtension.namespace);
                    });

                    return true;
                  } else return false;
                });
              });
            }.bind(this)
          );
        }

        //remove stencil properties
        if (jsonExtension.removeproperties) {
          jsonExtension.removeproperties.each(
            function (remprop) {
              var stencil = this.stencil(jsonExtension['extends'] + remprop.stencil);
              if (stencil) {
                remprop.properties.each(function (propId) {
                  stencil.removeProperty(propId);
                });
              }
            }.bind(this)
          );
        }

        //remove stencils
        if (jsonExtension.removestencils) {
          $A(jsonExtension.removestencils).each(
            function (remstencil) {
              delete this._availableStencils[jsonExtension['extends'] + remstencil];
            }.bind(this)
          );
        }
      }
    } catch (e) {
      ORYX.Log.debug('StencilSet.addExtension: Something went wrong when initialising the stencil set extension. ' + e);
    }
  },

  removeExtension: function (namespace) {
    var jsonExtension = this._extensions[namespace];
    if (jsonExtension) {
      //unload extension's stencils
      if (jsonExtension.stencils) {
        $A(jsonExtension.stencils).each(
          function (stencil) {
            var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this);
            delete this._stencils[oStencil.id()]; // maybe not ??
            delete this._availableStencils[oStencil.id()];
          }.bind(this)
        );
      }

      //unload extension's properties
      if (jsonExtension.properties) {
        var stencils = this._stencils.values();

        stencils.each(
          function (stencil) {
            var roles = stencil.roles();

            jsonExtension.properties.each(function (prop) {
              prop.roles.any(function (role) {
                role = jsonExtension['extends'] + role;
                if (roles.member(role)) {
                  prop.properties.each(function (property) {
                    stencil.removeProperty(property.id);
                  });

                  return true;
                } else return false;
              });
            });
          }.bind(this)
        );
      }

      //restore removed stencil properties
      if (jsonExtension.removeproperties) {
        jsonExtension.removeproperties.each(
          function (remprop) {
            var stencil = this.stencil(jsonExtension['extends'] + remprop.stencil);
            if (stencil) {
              var stencilJson = $A(this._jsonObject.stencils).find(function (s) {
                return s.id == stencil.id();
              });
              remprop.properties.each(
                function (propId) {
                  var propertyJson = $A(stencilJson.properties).find(function (p) {
                    return p.id == propId;
                  });
                  stencil.addProperty(propertyJson, this.namespace());
                }.bind(this)
              );
            }
          }.bind(this)
        );
      }

      //restore removed stencils
      if (jsonExtension.removestencils) {
        $A(jsonExtension.removestencils).each(
          function (remstencil) {
            var sId = jsonExtension['extends'] + remstencil;
            this._availableStencils[sId] = this._stencils[sId];
          }.bind(this)
        );
      }
    }
    delete this._extensions[namespace];
  },

  __handleStencilset: function (response) {
    try {
      // using eval instead of prototype's parsing,
      // since there are functions in this JSON.
      eval('this._jsonObject =' + response.responseText);
    } catch (e) {
      throw 'Stenciset corrupt: ' + e;
    }

    // assert it was parsed.
    if (!this._jsonObject) {
      throw 'Error evaluating stencilset. It may be corrupt.';
    }

    with (this._jsonObject) {
      // assert there is a namespace.
      if (!namespace || namespace === '') throw 'Namespace definition missing in stencilset.';

      if (!(stencils instanceof Array)) throw 'Stencilset corrupt.';

      // assert namespace ends with '#'.
      if (!namespace.endsWith('#')) namespace = namespace + '#';

      // assert title and description are strings.
      if (!title) title = '';
      if (!description) description = '';
    }
  },

  /**
   * This method is called when the HTTP request to get the requested stencil
   * set succeeds. The response is supposed to be a JSON representation
   * according to the stencil set specification.
   * @param {Object} response The JSON representation according to the
   * 			stencil set specification.
   */
  _init: function (response) {
    // init and check consistency.
    this.__handleStencilset(response);

    var pps = new Hash();

    // init property packages
    if (this._jsonObject.propertyPackages) {
      $A(this._jsonObject.propertyPackages).each(
        function (pp) {
          pps[pp.name] = pp.properties;
        }.bind(this)
      );
    }

    var defaultPosition = 0;

    // init each stencil
    $A(this._jsonObject.stencils).each(
      function (stencil) {
        defaultPosition++;

        // instantiate normally.
        var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this, pps, defaultPosition);
        this._stencils[oStencil.id()] = oStencil;
        this._availableStencils[oStencil.id()] = oStencil;
      }.bind(this)
    );
  },

  _cancelInit: function (response) {
    this.errornous = true;
  },

  toString: function () {
    return 'StencilSet ' + this.title() + ' (' + this.namespace() + ')';
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespace
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
  ORYX.Core.StencilSet = {};
}

/**
 * Class StencilSets
 * uses Prototpye 1.5.0
 * uses Inheritance
 *
 * Singleton
 */
//storage for loaded stencil sets by namespace
ORYX.Core.StencilSet._stencilSetsByNamespace = new Hash();

//storage for stencil sets by url
ORYX.Core.StencilSet._stencilSetsByUrl = new Hash();

//storage for stencil set namespaces by editor instances
ORYX.Core.StencilSet._StencilSetNSByEditorInstance = new Hash();

//storage for rules by editor instances
ORYX.Core.StencilSet._rulesByEditorInstance = new Hash();

/**
 *
 * @param {String} editorId
 *
 * @return {Hash} Returns a hash map with all stencil sets that are loaded by
 * 					the editor with the editorId.
 */
ORYX.Core.StencilSet.stencilSets = function (editorId) {
  var stencilSetNSs = ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId];
  var stencilSets = new Hash();
  if (stencilSetNSs) {
    stencilSetNSs.each(function (stencilSetNS) {
      var stencilSet = ORYX.Core.StencilSet.stencilSet(stencilSetNS);
      stencilSets[stencilSet.namespace()] = stencilSet;
    });
  }
  return stencilSets;
};

/**
 *
 * @param {String} namespace
 *
 * @return {ORYX.Core.StencilSet.StencilSet} Returns the stencil set with the specified
 * 										namespace.
 *
 * The method can handle namespace strings like
 *  http://www.example.org/stencilset
 *  http://www.example.org/stencilset#
 *  http://www.example.org/stencilset#ANode
 */
ORYX.Core.StencilSet.stencilSet = function (namespace) {
  ORYX.Log.trace('Getting stencil set %0', namespace);
  var splitted = namespace.split('#', 1);
  if (splitted.length === 1) {
    ORYX.Log.trace('Getting stencil set %0', splitted[0]);
    return ORYX.Core.StencilSet._stencilSetsByNamespace[splitted[0] + '#'];
  } else {
    return undefined;
  }
};

/**
 *
 * @param {String} id
 *
 * @return {ORYX.Core.StencilSet.Stencil} Returns the stencil specified by the id.
 *
 * The id must be unique and contains the namespace of the stencil's stencil set.
 * e.g. http://www.example.org/stencilset#ANode
 */
ORYX.Core.StencilSet.stencil = function (id) {
  ORYX.Log.trace('Getting stencil for %0', id);
  var ss = ORYX.Core.StencilSet.stencilSet(id);
  if (ss) {
    return ss.stencil(id);
  } else {
    ORYX.Log.trace('Cannot fild stencil for %0', id);
    return undefined;
  }
};

/**
 *
 * @param {String} editorId
 *
 * @return {ORYX.Core.StencilSet.Rules} Returns the rules object for the editor
 * 									specified by its editor id.
 */
ORYX.Core.StencilSet.rules = function (editorId) {
  if (!ORYX.Core.StencilSet._rulesByEditorInstance[editorId]) {
    ORYX.Core.StencilSet._rulesByEditorInstance[editorId] = new ORYX.Core.StencilSet.Rules();
  }
  return ORYX.Core.StencilSet._rulesByEditorInstance[editorId];
};

/**
 *
 * @param {String} url
 * @param {String} editorId
 *
 * Loads a stencil set from url, if it is not already loaded.
 * It also stores which editor instance loads the stencil set and
 * initializes the Rules object for the editor instance.
 */
ORYX.Core.StencilSet.loadStencilSet = function (url, modelMetaData, editorId) {
  // Alfresco: disable cache, because stencil sets are now flexible

  //var stencilSet = ORYX.Core.StencilSet._stencilSetsByUrl[url];

  //if(!stencilSet) {
  //load stencil set
  stencilSet = new ORYX.Core.StencilSet.StencilSet(url, modelMetaData, editorId);

  //store stencil set
  ORYX.Core.StencilSet._stencilSetsByNamespace[stencilSet.namespace()] = stencilSet;

  //store stencil set by url
  ORYX.Core.StencilSet._stencilSetsByUrl[url] = stencilSet;
  //}

  var namespace = stencilSet.namespace();

  //store which editorInstance loads the stencil set
  if (ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId]) {
    ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId].push(namespace);
  } else {
    ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId] = [namespace];
  }

  //store the rules for the editor instance
  if (ORYX.Core.StencilSet._rulesByEditorInstance[editorId]) {
    ORYX.Core.StencilSet._rulesByEditorInstance[editorId].initializeRules(stencilSet);
  } else {
    var rules = new ORYX.Core.StencilSet.Rules();
    rules.initializeRules(stencilSet);
    ORYX.Core.StencilSet._rulesByEditorInstance[editorId] = rules;
  }
};

/**
 * Returns the translation of an attribute in jsonObject specified by its name
 * according to navigator.language
 */
ORYX.Core.StencilSet.getTranslation = function (jsonObject, name) {
  var lang = ORYX.I18N.Language.toLowerCase();

  var result = jsonObject[name + '_' + lang];

  if (result) return result;

  result = jsonObject[name + '_' + lang.substr(0, 2)];

  if (result) return result;

  return jsonObject[name];
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription With Bounds you can set and get position and size of UIObjects.
 */
ORYX.Core.Command = Clazz.extend({
  /**
   * Constructor
   */
  construct: function () {},

  execute: function () {
    throw 'Command.execute() has to be implemented!';
  },

  rollback: function () {
    throw 'Command.rollback() has to be implemented!';
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription With Bounds you can set and get position and size of UIObjects.
 */
ORYX.Core.Bounds = {
  /**
   * Constructor
   */
  construct: function () {
    this._changedCallbacks = []; //register a callback with changedCallacks.push(this.method.bind(this));
    this.a = {};
    this.b = {};
    this.set.apply(this, arguments);
    this.suspendChange = false;
    this.changedWhileSuspend = false;
  },

  /**
   * Calls all registered callbacks.
   */
  _changed: function (sizeChanged) {
    if (!this.suspendChange) {
      this._changedCallbacks.each(
        function (callback) {
          callback(this, sizeChanged);
        }.bind(this)
      );
      this.changedWhileSuspend = false;
    } else this.changedWhileSuspend = true;
  },

  /**
   * Registers a callback that is called, if the bounds changes.
   * @param callback {Function} The callback function.
   */
  registerCallback: function (callback) {
    if (!this._changedCallbacks.member(callback)) {
      this._changedCallbacks.push(callback);
    }
  },

  /**
   * Unregisters a callback.
   * @param callback {Function} The callback function.
   */
  unregisterCallback: function (callback) {
    this._changedCallbacks = this._changedCallbacks.without(callback);
  },

  /**
   * Sets position and size of the shape dependent of four coordinates
   * (set(ax, ay, bx, by);), two points (set({x: ax, y: ay}, {x: bx, y: by});)
   * or one bound (set({a: {x: ax, y: ay}, b: {x: bx, y: by}});).
   */
  set: function () {
    var changed = false;

    switch (arguments.length) {
      case 1:
        if (this.a.x !== arguments[0].a.x) {
          changed = true;
          this.a.x = arguments[0].a.x;
        }
        if (this.a.y !== arguments[0].a.y) {
          changed = true;
          this.a.y = arguments[0].a.y;
        }
        if (this.b.x !== arguments[0].b.x) {
          changed = true;
          this.b.x = arguments[0].b.x;
        }
        if (this.b.y !== arguments[0].b.y) {
          changed = true;
          this.b.y = arguments[0].b.y;
        }
        break;

      case 2:
        var ax = Math.min(arguments[0].x, arguments[1].x);
        var ay = Math.min(arguments[0].y, arguments[1].y);
        var bx = Math.max(arguments[0].x, arguments[1].x);
        var by = Math.max(arguments[0].y, arguments[1].y);
        if (this.a.x !== ax) {
          changed = true;
          this.a.x = ax;
        }
        if (this.a.y !== ay) {
          changed = true;
          this.a.y = ay;
        }
        if (this.b.x !== bx) {
          changed = true;
          this.b.x = bx;
        }
        if (this.b.y !== by) {
          changed = true;
          this.b.y = by;
        }
        break;

      case 4:
        var ax = Math.min(arguments[0], arguments[2]);
        var ay = Math.min(arguments[1], arguments[3]);
        var bx = Math.max(arguments[0], arguments[2]);
        var by = Math.max(arguments[1], arguments[3]);
        if (this.a.x !== ax) {
          changed = true;
          this.a.x = ax;
        }
        if (this.a.y !== ay) {
          changed = true;
          this.a.y = ay;
        }
        if (this.b.x !== bx) {
          changed = true;
          this.b.x = bx;
        }
        if (this.b.y !== by) {
          changed = true;
          this.b.y = by;
        }
        break;
    }

    if (changed) {
      this._changed(true);
    }
  },

  /**
   * Moves the bounds so that the point p will be the new upper left corner.
   * @param {Point} p
   * or
   * @param {Number} x
   * @param {Number} y
   */
  moveTo: function () {
    var currentPosition = this.upperLeft();
    switch (arguments.length) {
      case 1:
        this.moveBy({
          x: arguments[0].x - currentPosition.x,
          y: arguments[0].y - currentPosition.y,
        });
        break;
      case 2:
        this.moveBy({
          x: arguments[0] - currentPosition.x,
          y: arguments[1] - currentPosition.y,
        });
        break;
      default:
      //TODO error
    }
  },

  /**
   * Moves the bounds relatively by p.
   * @param {Point} p
   * or
   * @param {Number} x
   * @param {Number} y
   *
   */
  moveBy: function () {
    var changed = false;

    switch (arguments.length) {
      case 1:
        var p = arguments[0];
        if (p.x !== 0 || p.y !== 0) {
          changed = true;
          this.a.x += p.x;
          this.b.x += p.x;
          this.a.y += p.y;
          this.b.y += p.y;
        }
        break;
      case 2:
        var x = arguments[0];
        var y = arguments[1];
        if (x !== 0 || y !== 0) {
          changed = true;
          this.a.x += x;
          this.b.x += x;
          this.a.y += y;
          this.b.y += y;
        }
        break;
      default:
      //TODO error
    }

    if (changed) {
      this._changed();
    }
  },

  /***
   * Includes the bounds b into the current bounds.
   * @param {Bounds} b
   */
  include: function (b) {
    if (this.a.x === undefined && this.a.y === undefined && this.b.x === undefined && this.b.y === undefined) {
      return b;
    }

    var cx = Math.min(this.a.x, b.a.x);
    var cy = Math.min(this.a.y, b.a.y);

    var dx = Math.max(this.b.x, b.b.x);
    var dy = Math.max(this.b.y, b.b.y);

    this.set(cx, cy, dx, dy);
  },

  /**
   * Relatively extends the bounds by p.
   * @param {Point} p
   */
  extend: function (p) {
    if (p.x !== 0 || p.y !== 0) {
      // this is over cross for the case that a and b have same coordinates.
      //((this.a.x > this.b.x) ? this.a : this.b).x += p.x;
      //((this.b.y > this.a.y) ? this.b : this.a).y += p.y;
      this.b.x += p.x;
      this.b.y += p.y;

      this._changed(true);
    }
  },

  /**
   * Widens the scope of the bounds by x.
   * @param {Number} x
   */
  widen: function (x) {
    if (x !== 0) {
      this.suspendChange = true;
      this.moveBy({ x: -x, y: -x });
      this.extend({ x: 2 * x, y: 2 * x });
      this.suspendChange = false;
      if (this.changedWhileSuspend) {
        this._changed(true);
      }
    }
  },

  /**
   * Returns the upper left corner's point regardless of the
   * bound delimiter points.
   */
  upperLeft: function () {
    var result = {};
    result.x = this.a.x;
    result.y = this.a.y;
    return result;
  },

  /**
   * Returns the lower Right left corner's point regardless of the
   * bound delimiter points.
   */
  lowerRight: function () {
    var result = {};
    result.x = this.b.x;
    result.y = this.b.y;
    return result;
  },

  /**
   * @return {Number} Width of bounds.
   */
  width: function () {
    return this.b.x - this.a.x;
  },

  /**
   * @return {Number} Height of bounds.
   */
  height: function () {
    return this.b.y - this.a.y;
  },

  /**
   * @return {Point} The center point of this bounds.
   */
  center: function () {
    var center = {};
    center.x = (this.a.x + this.b.x) / 2.0;
    center.y = (this.a.y + this.b.y) / 2.0;
    return center;
  },

  /**
   * @return {Point} The center point of this bounds relative to upperLeft.
   */
  midPoint: function () {
    var midpoint = {};
    midpoint.x = (this.b.x - this.a.x) / 2.0;
    midpoint.y = (this.b.y - this.a.y) / 2.0;
    return midpoint;
  },

  /**
   * Moves the center point of this bounds to the new position.
   * @param p {Point}
   * or
   * @param x {Number}
   * @param y {Number}
   */
  centerMoveTo: function () {
    var currentPosition = this.center();

    switch (arguments.length) {
      case 1:
        this.moveBy(arguments[0].x - currentPosition.x, arguments[0].y - currentPosition.y);
        break;

      case 2:
        this.moveBy(arguments[0] - currentPosition.x, arguments[1] - currentPosition.y);
        break;
    }
  },

  isIncluded: function (point, offset) {
    var pointX, pointY, offset;

    // Get the the two Points
    switch (arguments.length) {
      case 1:
        pointX = arguments[0].x;
        pointY = arguments[0].y;
        offset = 0;

        break;
      case 2:
        if (arguments[0].x && arguments[0].y) {
          pointX = arguments[0].x;
          pointY = arguments[0].y;
          offset = Math.abs(arguments[1]);
        } else {
          pointX = arguments[0];
          pointY = arguments[1];
          offset = 0;
        }
        break;
      case 3:
        pointX = arguments[0];
        pointY = arguments[1];
        offset = Math.abs(arguments[2]);
        break;
      default:
        throw 'isIncluded needs one, two or three arguments';
    }

    var ul = this.upperLeft();
    var lr = this.lowerRight();

    if (pointX >= ul.x - offset && pointX <= lr.x + offset && pointY >= ul.y - offset && pointY <= lr.y + offset) return true;
    else return false;
  },

  /**
   * @return {Bounds} A copy of this bounds.
   */
  clone: function () {
    //Returns a new bounds object without the callback
    // references of the original bounds
    return new ORYX.Core.Bounds(this);
  },

  toString: function () {
    return '( ' + this.a.x + ' | ' + this.a.y + ' )/( ' + this.b.x + ' | ' + this.b.y + ' )';
  },

  serializeForERDF: function () {
    return this.a.x + ',' + this.a.y + ',' + this.b.x + ',' + this.b.y;
  },
};

ORYX.Core.Bounds = Clazz.extend(ORYX.Core.Bounds);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription Abstract base class for all objects that have a graphical representation
 * within the editor.
 * @extends Clazz
 */
ORYX.Core.UIObject = {
  /**
   * Constructor of the UIObject class.
   */
  construct: function (options) {
    this.isChanged = true; //Flag, if UIObject has been changed since last update.
    this.isResized = true;
    this.isVisible = true; //Flag, if UIObject's display attribute is set to 'inherit' or 'none'
    this.isSelectable = false; //Flag, if UIObject is selectable.
    this.isResizable = false; //Flag, if UIObject is resizable.
    this.isMovable = false; //Flag, if UIObject is movable.

    this.id = ORYX.Editor.provideId(); //get unique id
    this.parent = undefined; //parent is defined, if this object is added to another uiObject.
    this.node = undefined; //this is a reference to the SVG representation, either locally or in DOM.
    this.children = []; //array for all add uiObjects

    this.bounds = new ORYX.Core.Bounds(); //bounds with undefined values

    this._changedCallback = this._changed.bind(this); //callback reference for calling _changed
    this.bounds.registerCallback(this._changedCallback); //set callback in bounds

    if (options && options.eventHandlerCallback) {
      this.eventHandlerCallback = options.eventHandlerCallback;
    }
  },

  /**
   * Sets isChanged flag to true. Callback for the bounds object.
   */
  _changed: function (bounds, isResized) {
    this.isChanged = true;
    if (this.bounds == bounds) this.isResized = isResized || this.isResized;
  },

  /**
   * If something changed, this method calls the refresh method that must be implemented by subclasses.
   */
  update: function () {
    if (this.isChanged) {
      this.refresh();
      this.isChanged = false;

      //call update of all children
      this.children.each(function (value) {
        value.update();
      });
    }
  },

  /**
   * Is called in update method, if isChanged is set to true. Sub classes should call the super class method.
   */
  refresh: function () {},

  /**
   * @return {Array} Array of all child UIObjects.
   */
  getChildren: function () {
    return this.children.clone();
  },

  /**
   * @return {Array} Array of all parent UIObjects.
   */
  getParents: function () {
    var parents = [];
    var parent = this.parent;
    while (parent) {
      parents.push(parent);
      parent = parent.parent;
    }
    return parents;
  },

  /**
   * Returns TRUE if the given parent is one of the UIObjects parents or the UIObject themselves, otherwise FALSE.
   * @param {UIObject} parent
   * @return {Boolean}
   */
  isParent: function (parent) {
    var cparent = this;
    while (cparent) {
      if (cparent === parent) {
        return true;
      }
      cparent = cparent.parent;
    }
    return false;
  },

  /**
   * @return {String} Id of this UIObject
   */
  getId: function () {
    return this.id;
  },

  /**
   * Method for accessing child uiObjects by id.
   * @param {String} id
   * @param {Boolean} deep
   *
   * @return {UIObject} If found, it returns the UIObject with id.
   */
  getChildById: function (id, deep) {
    return this.children.find(function (uiObj) {
      if (uiObj.getId() === id) {
        return uiObj;
      } else {
        if (deep) {
          var obj = uiObj.getChildById(id, deep);
          if (obj) {
            return obj;
          }
        }
      }
    });
  },

  /**
   * Adds an UIObject to this UIObject and sets the parent of the
   * added UIObject. It is also added to the SVG representation of this
   * UIObject.
   * @param {UIObject} uiObject
   */
  add: function (uiObject) {
    //add uiObject, if it is not already a child of this object
    if (!this.children.member(uiObject)) {
      //if uiObject is child of another parent, remove it from that parent.
      if (uiObject.parent) {
        uiObject.remove(uiObject);
      }

      //add uiObject to children
      this.children.push(uiObject);

      //set parent reference
      uiObject.parent = this;

      //add uiObject.node to this.node
      uiObject.node = this.node.appendChild(uiObject.node);

      //register callback to get informed, if child is changed
      uiObject.bounds.registerCallback(this._changedCallback);

      //uiObject.update();
    } else {
      ORYX.Log.info('add: ORYX.Core.UIObject is already a child of this object.');
    }
  },

  /**
   * Removes UIObject from this UIObject. The SVG representation will also
   * be removed from this UIObject's SVG representation.
   * @param {UIObject} uiObject
   */
  remove: function (uiObject) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      this.children = this._uiObjects.without(uiObject);

      //delete parent reference of uiObject
      uiObject.parent = undefined;

      //delete uiObject.node from this.node
      uiObject.node = this.node.removeChild(uiObject.node);

      //unregister callback to get informed, if child is changed
      uiObject.bounds.unregisterCallback(this._changedCallback);
    } else {
      ORYX.Log.info('remove: ORYX.Core.UIObject is not a child of this object.');
    }
  },

  /**
   * Calculates absolute bounds of this UIObject.
   */
  absoluteBounds: function () {
    if (this.parent) {
      var absUL = this.absoluteXY();
      return new ORYX.Core.Bounds(absUL.x, absUL.y, absUL.x + this.bounds.width(), absUL.y + this.bounds.height());
    } else {
      return this.bounds.clone();
    }
  },

  /**
   * @return {Point} The absolute position of this UIObject.
   */
  absoluteXY: function () {
    if (this.parent) {
      var pXY = this.parent.absoluteXY();
      var result = {};
      result.x = pXY.x + this.bounds.upperLeft().x;
      result.y = pXY.y + this.bounds.upperLeft().y;
      return result;
    } else {
      var result = {};
      result.x = this.bounds.upperLeft().x;
      result.y = this.bounds.upperLeft().y;
      return result;
    }
  },

  /**
   * @return {Point} The absolute position from the Center of this UIObject.
   */
  absoluteCenterXY: function () {
    if (this.parent) {
      var pXY = this.parent.absoluteXY();
      var result = {};
      result.x = pXY.x + this.bounds.center().x;
      result.y = pXY.y + this.bounds.center().y;
      return result;
    } else {
      var result = {};
      result.x = this.bounds.center().x;
      result.y = this.bounds.center().y;
      return result;
    }
  },

  /**
   * Hides this UIObject and all its children.
   */
  hide: function () {
    this.node.setAttributeNS(null, 'display', 'none');
    this.isVisible = false;
    this.children.each(function (uiObj) {
      uiObj.hide();
    });
  },

  /**
   * Enables visibility of this UIObject and all its children.
   */
  show: function () {
    this.node.setAttributeNS(null, 'display', 'inherit');
    this.isVisible = true;
    this.children.each(function (uiObj) {
      uiObj.show();
    });
  },

  addEventHandlers: function (node) {
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this._delegateEvent.bind(this), false);
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this._delegateEvent.bind(this), false);
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this._delegateEvent.bind(this), false);
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOVER, this._delegateEvent.bind(this), false);
    node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOUT, this._delegateEvent.bind(this), false);
    node.addEventListener('click', this._delegateEvent.bind(this), false);
    node.addEventListener(ORYX.CONFIG.EVENT_DBLCLICK, this._delegateEvent.bind(this), false);
  },

  _delegateEvent: function (event) {
    if (this.eventHandlerCallback) {
      this.eventHandlerCallback(event, this);
    }
  },

  toString: function () {
    return 'UIObject ' + this.id;
  },
};
ORYX.Core.UIObject = Clazz.extend(ORYX.Core.UIObject);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * Top Level uiobject.
 * @class ORYX.Core.AbstractShape
 * @extends ORYX.Core.UIObject
 */
ORYX.Core.AbstractShape = ORYX.Core.UIObject.extend(
  /** @lends ORYX.Core.AbstractShape.prototype */
  {
    /**
     * Constructor
     */
    construct: function (options, stencil, facade) {
      arguments.callee.$.construct.apply(this, arguments);

      this.resourceId = ORYX.Editor.provideId(); //Id of resource in DOM

      // stencil reference
      this._stencil = stencil;
      // if the stencil defines a super stencil that should be used for its instances, set it.
      if (this._stencil._jsonStencil.superId) {
        stencilId = this._stencil.id();
        superStencilId = stencilId.substring(0, stencilId.indexOf('#') + 1) + stencil._jsonStencil.superId;
        stencilSet = this._stencil.stencilSet();
        this._stencil = stencilSet.stencil(superStencilId);
      }

      //Hash map for all properties. Only stores the values of the properties.
      this.properties = new Hash();
      this.propertiesChanged = new Hash();

      // List of properties which are not included in the stencilset,
      // but which gets (de)serialized
      this.hiddenProperties = new Hash();

      //Initialization of property map and initial value.
      this._stencil.properties().each(
        function (property) {
          var key = property.prefix() + '-' + property.id();
          this.properties[key] = property.value();
          this.propertiesChanged[key] = true;
        }.bind(this)
      );

      // if super stencil was defined, also regard stencil's properties:
      if (stencil._jsonStencil.superId) {
        stencil.properties().each(
          function (property) {
            var key = property.prefix() + '-' + property.id();
            var value = property.value();
            var oldValue = this.properties[key];
            this.properties[key] = value;
            this.propertiesChanged[key] = true;

            // Raise an event, to show that the property has changed
            // required for plugins like processLink.js
            //window.setTimeout( function(){

            this._delegateEvent({
              type: ORYX.CONFIG.EVENT_PROPERTY_CHANGED,
              name: key,
              value: value,
              oldValue: oldValue,
            });

            //}.bind(this), 10)
          }.bind(this)
        );
      }
    },

    layout: function () {},

    /**
     * Returns the stencil object specifiing the type of the shape.
     */
    getStencil: function () {
      return this._stencil;
    },

    /**
     *
     * @param {Object} resourceId
     */
    getChildShapeByResourceId: function (resourceId) {
      resourceId = ERDF.__stripHashes(resourceId);

      return this.getChildShapes(true).find(function (shape) {
        return shape.resourceId == resourceId;
      });
    },
    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildShapes: function (deep, iterator) {
      var result = [];

      this.children.each(function (uiObject) {
        if (uiObject instanceof ORYX.Core.Shape && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject);
          }
          result.push(uiObject);
          if (deep) {
            result = result.concat(uiObject.getChildShapes(deep, iterator));
          }
        }
      });

      return result;
    },

    /**
     * @param {Object} shape
     * @return {boolean} true if any of shape's childs is given shape
     */
    hasChildShape: function (shape) {
      return this.getChildShapes().any(function (child) {
        return child === shape || child.hasChildShape(shape);
      });
    },

    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildNodes: function (deep, iterator) {
      var result = [];

      this.children.each(function (uiObject) {
        if (uiObject instanceof ORYX.Core.Node && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject);
          }
          result.push(uiObject);
        }
        if (uiObject instanceof ORYX.Core.Shape) {
          if (deep) {
            result = result.concat(uiObject.getChildNodes(deep, iterator));
          }
        }
      });

      return result;
    },

    /**
     *
     * @param {Object} deep
     * @param {Object} iterator
     */
    getChildEdges: function (deep, iterator) {
      var result = [];

      this.children.each(function (uiObject) {
        if (uiObject instanceof ORYX.Core.Edge && uiObject.isVisible) {
          if (iterator) {
            iterator(uiObject);
          }
          result.push(uiObject);
        }
        if (uiObject instanceof ORYX.Core.Shape) {
          if (deep) {
            result = result.concat(uiObject.getChildEdges(deep, iterator));
          }
        }
      });

      return result;
    },

    /**
     * Returns a sorted array of ORYX.Core.Node objects.
     * Ordered in z Order, the last object has the highest z Order.
     */
    //TODO deep iterator
    getAbstractShapesAtPosition: function () {
      var x, y;
      switch (arguments.length) {
        case 1:
          x = arguments[0].x;
          y = arguments[0].y;
          break;
        case 2: //two or more arguments
          x = arguments[0];
          y = arguments[1];
          break;
        default:
          throw 'getAbstractShapesAtPosition needs 1 or 2 arguments!';
      }

      if (this.isPointIncluded(x, y)) {
        var result = [];
        result.push(this);

        //check, if one child is at that position

        var childNodes = this.getChildNodes();
        var childEdges = this.getChildEdges();

        [childNodes, childEdges].each(function (ne) {
          var nodesAtPosition = new Hash();

          ne.each(function (node) {
            if (!node.isVisible) {
              return;
            }
            var candidates = node.getAbstractShapesAtPosition(x, y);
            if (candidates.length > 0) {
              var nodesInZOrder = $A(node.node.parentNode.childNodes);
              var zOrderIndex = nodesInZOrder.indexOf(node.node);
              nodesAtPosition[zOrderIndex] = candidates;
            }
          });

          nodesAtPosition
            .keys()
            .sort()
            .each(function (key) {
              result = result.concat(nodesAtPosition[key]);
            });
        });

        return result;
      } else {
        return [];
      }
    },

    /**
     *
     * @param key {String} Must be 'prefix-id' of property
     * @param value {Object} Can be of type String or Number according to property type.
     */
    setProperty: function (key, value, force) {
      var oldValue = this.properties[key];
      if (oldValue !== value || force === true) {
        this.properties[key] = value;
        this.propertiesChanged[key] = true;
        this._changed();

        // Raise an event, to show that the property has changed
        //window.setTimeout( function(){

        if (!this._isInSetProperty) {
          this._isInSetProperty = true;

          this._delegateEvent({
            type: ORYX.CONFIG.EVENT_PROPERTY_CHANGED,
            elements: [this],
            name: key,
            value: value,
            oldValue: oldValue,
          });

          delete this._isInSetProperty;
        }
        //}.bind(this), 10)
      }
    },

    /**
     * Returns TRUE if one of the properties is flagged as dirty
     * @return {boolean}
     */
    isPropertyChanged: function () {
      return this.propertiesChanged.any(function (property) {
        return property.value;
      });
    },

    /**
     *
     * @param {String} Must be 'prefix-id' of property
     * @param {Object} Can be of type String or Number according to property type.
     */
    setHiddenProperty: function (key, value) {
      // IF undefined, Delete
      if (value === undefined) {
        delete this.hiddenProperties[key];
        return;
      }
      var oldValue = this.hiddenProperties[key];
      if (oldValue !== value) {
        this.hiddenProperties[key] = value;
      }
    },
    /**
     * Calculate if the point is inside the Shape
     * @param {Point}
     */
    isPointIncluded: function (pointX, pointY, absoluteBounds) {
      var absBounds = absoluteBounds ? absoluteBounds : this.absoluteBounds();
      return absBounds.isIncluded(pointX, pointY);
    },

    /**
     * Get the serialized object
     * return Array with hash-entrees (prefix, name, value)
     * Following values will given:
     * 		Type
     * 		Properties
     */
    serialize: function () {
      var serializedObject = [];

      // Add the type
      serializedObject.push({ name: 'type', prefix: 'oryx', value: this.getStencil().id(), type: 'literal' });

      // Add hidden properties
      this.hiddenProperties.each(
        function (prop) {
          serializedObject.push({ name: prop.key.replace('oryx-', ''), prefix: 'oryx', value: prop.value, type: 'literal' });
        }.bind(this)
      );

      // Add all properties
      this.getStencil()
        .properties()
        .each(
          function (property) {
            var prefix = property.prefix(); // Get prefix
            var name = property.id(); // Get name

            //if(typeof this.properties[prefix+'-'+name] == 'boolean' || this.properties[prefix+'-'+name] != "")
            serializedObject.push({ name: name, prefix: prefix, value: this.properties[prefix + '-' + name], type: 'literal' });
          }.bind(this)
        );

      return serializedObject;
    },

    deserialize: function (serialize) {
      // Search in Serialize
      var initializedDocker = 0;

      // Sort properties so that the hidden properties are first in the list
      serialize = serialize.sort(
        function (a, b) {
          a = Number(this.properties.keys().member(a.prefix + '-' + a.name));
          b = Number(this.properties.keys().member(b.prefix + '-' + b.name));
          return a > b ? 1 : a < b ? -1 : 0;
        }.bind(this)
      );

      serialize.each(
        function (obj) {
          var name = obj.name;
          var prefix = obj.prefix;
          var value = obj.value;

          // Complex properties can be real json objects, encode them to a string
          if (Object.prototype.toString.call(value) === 'Object') value = JSON.stringify(value);

          switch (prefix + '-' + name) {
            case 'raziel-parent':
              // Set parent
              if (!this.parent) {
                break;
              }

              // Set outgoing Shape
              var parent = this.getCanvas().getChildShapeByResourceId(value);
              if (parent) {
                parent.add(this);
              }

              break;
            default:
              // If list, eval as an array
              var prop = this.getStencil().property(prefix + '-' + name);
              if (prop && prop.isList() && typeof value === 'string') {
                if ((value || '').strip() && !value.startsWith('[') && !value.startsWith(']')) value = '["' + value.strip() + '"]';
                value = ((value || '').strip() || '[]').evalJSON();
              }

              // Set property
              if (this.properties.keys().member(prefix + '-' + name)) {
                this.setProperty(prefix + '-' + name, value);
              } else if (
                !(
                  name === 'bounds' ||
                  name === 'parent' ||
                  name === 'target' ||
                  name === 'dockers' ||
                  name === 'docker' ||
                  name === 'outgoing' ||
                  name === 'incoming'
                )
              ) {
                this.setHiddenProperty(prefix + '-' + name, value);
              }
          }
        }.bind(this)
      );
    },

    toString: function () {
      return 'ORYX.Core.AbstractShape ' + this.id;
    },

    /**
     * Converts the shape to a JSON representation.
     * @return {Object} A JSON object with included ORYX.Core.AbstractShape.JSONHelper and getShape() method.
     */
    toJSON: function () {
      var json = {
        resourceId: this.resourceId,
        properties: jQuery.extend({}, this.properties, this.hiddenProperties).inject(
          {},
          function (props, prop) {
            var key = prop[0];
            var value = prop[1];

            //If complex property, value should be a json object
            if (
              this.getStencil().property(key) &&
              this.getStencil().property(key).type() === ORYX.CONFIG.TYPE_COMPLEX &&
              Object.prototype.toString.call(value) === 'String'
            ) {
              try {
                value = JSON.parse(value);
              } catch (error) {}
              //try {value = JSON.parse(value);} catch(error){}

              // Parse date
            } else if (value instanceof Date && this.getStencil().property(key)) {
              try {
                value = value.format(this.getStencil().property(key).dateFormat());
              } catch (e) {}
            }

            //Takes "my_property" instead of "oryx-my_property" as key
            key = key.replace(/^[\w_]+-/, '');
            props[key] = value;

            return props;
          }.bind(this)
        ),
        stencil: {
          id: this.getStencil().idWithoutNs(),
        },
        childShapes: this.getChildShapes().map(function (shape) {
          return shape.toJSON();
        }),
      };

      if (this.getOutgoingShapes) {
        json.outgoing = this.getOutgoingShapes().map(function (shape) {
          return {
            resourceId: shape.resourceId,
          };
        });
      }

      if (this.bounds) {
        json.bounds = {
          lowerRight: this.bounds.lowerRight(),
          upperLeft: this.bounds.upperLeft(),
        };
      }

      if (this.dockers) {
        json.dockers = this.dockers.map(function (docker) {
          var d = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center();
          d.getDocker = function () {
            return docker;
          };
          return d;
        });
      }

      jQuery.extend(json, ORYX.Core.AbstractShape.JSONHelper);

      // do not pollute the json attributes (for serialization), so put the corresponding
      // shape is encapsulated in a method
      json.getShape = function () {
        return this;
      }.bind(this);

      return json;
    },
  }
);

/**
 * @namespace Collection of methods which can be used on a shape json object (ORYX.Core.AbstractShape#toJSON()).
 * @example
 * jQuery.extend(shapeAsJson, ORYX.Core.AbstractShape.JSONHelper);
 */
ORYX.Core.AbstractShape.JSONHelper = {
  /**
   * Iterates over each child shape.
   * @param {Object} iterator Iterator function getting a child shape and his parent as arguments.
   * @param {boolean} [deep=false] Iterate recursively (childShapes of childShapes)
   * @param {boolean} [modify=false] If true, the result of the iterator function is taken as new shape, return false to delete it. This enables modifying the object while iterating through the child shapes.
   * @example
   * // Increases the lowerRight x value of each direct child shape by one.
   * myShapeAsJson.eachChild(function(shape, parentShape){
   *     shape.bounds.lowerRight.x = shape.bounds.lowerRight.x + 1;
   *     return shape;
   * }, false, true);
   */
  eachChild: function (iterator, deep, modify) {
    if (!this.childShapes) return;

    var newChildShapes = []; //needed if modify = true

    this.childShapes.each(
      function (shape) {
        if (!(shape.eachChild instanceof Function)) {
          jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper);
        }
        var res = iterator(shape, this);
        if (res) newChildShapes.push(res); //if false is returned, and modify = true, current shape is deleted.

        if (deep) shape.eachChild(iterator, deep, modify);
      }.bind(this)
    );

    if (modify) this.childShapes = newChildShapes;
  },

  getShape: function () {
    return null;
  },
  getChildShapes: function (deep) {
    var allShapes = this.childShapes;

    if (deep) {
      this.eachChild(function (shape) {
        if (!(shape.getChildShapes instanceof Function)) {
          jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper);
        }
        allShapes = allShapes.concat(shape.getChildShapes(deep));
      }, true);
    }

    return allShapes;
  },

  /**
   * @return {String} Serialized JSON object
   */
  serialize: function () {
    return JSON.stringify(this);
  },
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}

/**
   @namespace Namespace for the Oryx core elements.
   @name ORYX.Core
*/
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @class Oryx canvas.
 * @extends ORYX.Core.AbstractShape
 *
 */
ORYX.Core.Canvas = ORYX.Core.AbstractShape.extend({
  /** @lends ORYX.Core.Canvas.prototype */

  /**
   * Defines the current zoom level
   */
  zoomLevel: 1,

  /**
   * Constructor
   */
  construct: function (options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments);

    if (!(options && options.width && options.height)) {
      ORYX.Log.fatal('Canvas is missing mandatory parameters options.width and options.height.');
      return;
    }
    this.facade = facade;
    //TODO: set document resource id
    this.resourceId = options.id;

    this.nodes = [];

    this.edges = [];

    // Row highlighting states
    this.colHighlightState = 0;

    this.colHighlightEnabled = false;

    //init svg document
    this.rootNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', options.parentNode, [
      'svg',
      { id: this.id, width: options.width, height: options.height },
      ['defs', {}],
    ]);

    this.rootNode.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
    this.rootNode.setAttribute('xmlns:svg', 'http://www.w3.org/2000/svg');

    this._htmlContainer = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', options.parentNode, [
      'div',
      { id: 'oryx_canvas_htmlContainer', style: 'position:absolute; top:5px' },
    ]);

    // Additional SVG-node BELOW the stencils to allow underlays (if that is even a word) by plugins
    this.underlayNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.rootNode, ['svg', { id: 'underlay-container' }]);

    // Create 2 svg-elements in the svg-container
    this.columnHightlight1 = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.underlayNode, [
      'rect',
      { x: 0, width: ORYX.CONFIG.FORM_ROW_WIDTH + 35, height: '100%', style: 'fill: #fff6d5', visibility: 'hidden' },
    ]);

    this.columnHightlight2 = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.underlayNode, [
      'rect',
      {
        x: ORYX.CONFIG.FORM_ROW_WIDTH + 35,
        width: ORYX.CONFIG.FORM_ROW_WIDTH + 25,
        height: '100%',
        style: 'fill: #fff6d5',
        visibility: 'hidden',
      },
    ]);

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.rootNode, [
      'g',
      {},
      ['g', { class: 'stencils' }, ['g', { class: 'me' }], ['g', { class: 'children' }], ['g', { class: 'edge' }]],
      ['g', { class: 'svgcontainer' }],
    ]);

    /*
		var off = 2 * ORYX.CONFIG.GRID_DISTANCE;
		var size = 3;
		var d = "";
		for(var i = 0; i <= options.width; i += off)
			for(var j = 0; j <= options.height; j += off)
				d = d + "M" + (i - size) + " " + j + " l" + (2*size) + " 0 m" + (-size) + " " + (-size) + " l0 " + (2*size) + " m0" + (-size) + " ";
							
		ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node.firstChild.firstChild,
			['path', {d:d , stroke:'#000000', 'stroke-width':'0.15px'},]);
		*/

    //Global definition of default font for shapes
    //Definitions in the SVG definition of a stencil will overwrite these settings for
    // that stencil.
    /*if(navigator.platform.indexOf("Mac") > -1) {
			this.node.setAttributeNS(null, 'stroke', 'black');
			this.node.setAttributeNS(null, 'stroke-width', '0.5px');
			this.node.setAttributeNS(null, 'font-family', 'Skia');
			//this.node.setAttributeNS(null, 'letter-spacing', '2px');
			this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT);
		} else {
			this.node.setAttributeNS(null, 'stroke', 'none');
			this.node.setAttributeNS(null, 'font-family', 'Verdana');
			this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT);
		}*/

    this.node.setAttributeNS(null, 'stroke', 'none');
    this.node.setAttributeNS(null, 'font-family', 'Verdana, sans-serif');
    this.node.setAttributeNS(null, 'font-size-adjust', 'none');
    this.node.setAttributeNS(null, 'font-style', 'normal');
    this.node.setAttributeNS(null, 'font-variant', 'normal');
    this.node.setAttributeNS(null, 'font-weight', 'normal');
    this.node.setAttributeNS(null, 'line-heigth', 'normal');

    this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT);

    this.bounds.set(0, 0, options.width, options.height);

    this.addEventHandlers(this.rootNode.parentNode);

    //disable context menu
    this.rootNode.oncontextmenu = function () {
      return false;
    };
  },

  focus: function () {
    try {
      // Get a href
      if (!this.focusEl) {
        this.focusEl = jQuery('body').append(jQuery('<a href="#" class="x-grid3-focus x-grid3-focus-canvas"/>'));
        this.focusEl.swallowEvent('click', true);
      }

      // Focus it
      this.focusEl.focus.defer(1, this.focusEl);
      this.focusEl.blur.defer(3, this.focusEl);
    } catch (e) {}
  },

  setHightlightState: function (state) {
    if (this.colHighlightEnabled && this.colHighlightState != state) {
      if (state == 0) {
        this.columnHightlight1.setAttribute('visibility', 'hidden');
        this.columnHightlight2.setAttribute('visibility', 'hidden');
      } else if (state == 1) {
        this.columnHightlight1.setAttribute('visibility', 'visible');
        this.columnHightlight2.setAttribute('visibility', 'hidden');
      } else if (state == 2) {
        this.columnHightlight1.setAttribute('visibility', 'hidden');
        this.columnHightlight2.setAttribute('visibility', 'visible');
      } else if (state == 3) {
        this.columnHightlight1.setAttribute('visibility', 'visible');
        this.columnHightlight2.setAttribute('visibility', 'visible');
      }
      this.colHighlightState = state;
    }
  },

  setHightlightStateBasedOnX: function (x) {
    if (x > ORYX.CONFIG.FORM_ROW_WIDTH + 30) {
      this.setHightlightState(2);
    } else {
      this.setHightlightState(1);
    }
  },

  update: function () {
    this.nodes.each(
      function (node) {
        this._traverseForUpdate(node);
      }.bind(this)
    );

    // call stencil's layout callback
    // (needed for row layouting of xforms)
    //this.getStencil().layout(this);

    var layoutEvents = this.getStencil().layout();

    if (layoutEvents) {
      layoutEvents.each(
        function (event) {
          // setup additional attributes
          event.shape = this;
          event.forceExecution = true;
          event.target = this.rootNode;

          // do layouting

          this._delegateEvent(event);
        }.bind(this)
      );
    }

    this.nodes.invoke('_update');

    this.edges.invoke('_update', true);

    /*this.children.each(function(child) {
			child._update();
		});*/
  },

  _traverseForUpdate: function (shape) {
    var childRet = shape.isChanged;
    shape.getChildNodes(
      false,
      function (child) {
        if (this._traverseForUpdate(child)) {
          childRet = true;
        }
      }.bind(this)
    );

    if (childRet) {
      shape.layout();
      return true;
    } else {
      return false;
    }
  },

  layout: function () {},

  /**
   *
   * @param {Object} deep
   * @param {Object} iterator
   */
  getChildNodes: function (deep, iterator) {
    if (!deep && !iterator) {
      return this.nodes.clone();
    } else {
      var result = [];
      this.nodes.each(function (uiObject) {
        if (iterator) {
          iterator(uiObject);
        }
        result.push(uiObject);

        if (deep && uiObject instanceof ORYX.Core.Shape) {
          result = result.concat(uiObject.getChildNodes(deep, iterator));
        }
      });

      return result;
    }
  },

  /**
   * buggy crap! use base class impl instead!
   * @param {Object} iterator
   */
  /*	getChildEdges: function(iterator) {
		if(iterator) {
			this.edges.each(function(edge) {
				iterator(edge);
			});
		}
		
		return this.edges.clone();
	},
*/
  /**
   * Overrides the UIObject.add method. Adds uiObject to the correct sub node.
   * @param {UIObject} uiObject
   */
  add: function (uiObject, index, silent) {
    //if uiObject is child of another UIObject, remove it.
    if (uiObject instanceof ORYX.Core.UIObject) {
      if (!this.children.member(uiObject)) {
        //if uiObject is child of another parent, remove it from that parent.
        if (uiObject.parent) {
          uiObject.parent.remove(uiObject, true);
        }

        //add uiObject to the Canvas
        //add uiObject to this Shape
        if (index != undefined) this.children.splice(index, 0, uiObject);
        else this.children.push(uiObject);

        //set parent reference
        uiObject.parent = this;

        //add uiObject.node to this.node depending on the type of uiObject
        if (uiObject instanceof ORYX.Core.Shape) {
          if (uiObject instanceof ORYX.Core.Edge) {
            uiObject.addMarkers(this.rootNode.getElementsByTagNameNS(NAMESPACE_SVG, 'defs')[0]);
            uiObject.node = this.node.childNodes[0].childNodes[2].appendChild(uiObject.node);
            this.edges.push(uiObject);
          } else {
            uiObject.node = this.node.childNodes[0].childNodes[1].appendChild(uiObject.node);
            this.nodes.push(uiObject);
          }
        } else {
          //UIObject
          uiObject.node = this.node.appendChild(uiObject.node);
        }

        uiObject.bounds.registerCallback(this._changedCallback);

        if (this.eventHandlerCallback && silent !== true)
          this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEADDED, shape: uiObject });
      } else {
        ORYX.Log.warn('add: ORYX.Core.UIObject is already a child of this object.');
      }
    } else {
      ORYX.Log.fatal('add: Parameter is not of type ORYX.Core.UIObject.');
    }
  },

  /**
   * Overrides the UIObject.remove method. Removes uiObject.
   * @param {UIObject} uiObject
   */
  remove: function (uiObject, silent) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      var parent = uiObject.parent;

      this.children = this.children.without(uiObject);

      //delete parent reference of uiObject
      uiObject.parent = undefined;

      //delete uiObject.node from this.node
      if (uiObject instanceof ORYX.Core.Shape) {
        if (uiObject instanceof ORYX.Core.Edge) {
          uiObject.removeMarkers();
          uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node);
          this.edges = this.edges.without(uiObject);
        } else {
          uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node);
          this.nodes = this.nodes.without(uiObject);
        }
      } else {
        //UIObject
        uiObject.node = this.node.removeChild(uiObject.node);
      }

      if (this.eventHandlerCallback && silent !== true)
        this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEREMOVED, shape: uiObject, parent: parent });

      uiObject.bounds.unregisterCallback(this._changedCallback);
    } else {
      ORYX.Log.warn('remove: ORYX.Core.UIObject is not a child of this object.');
    }
  },

  /**
     * Creates shapes out of the given collection of shape objects and adds them to the canvas.
     * @example 
     * canvas.addShapeObjects({
         bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } },
         resourceId:"oryx_F0715955-50F2-403D-9851-C08CFE70F8BD",
         childShapes:[],
         properties:{},
         stencil:{
           id:"Subprocess"
         },
         outgoing:[{resourceId: 'aShape'}],
         target: {resourceId: 'aShape'}
       });
     * @param {Object} shapeObjects 
     * @param {Function} [eventHandler] An event handler passed to each newly created shape (as eventHandlerCallback)
     * @return {Array} A collection of ORYX.Core.Shape
     * @methodOf ORYX.Core.Canvas.prototype
     */
  addShapeObjects: function (shapeObjects, eventHandler) {
    if (!shapeObjects) return;

    this.initializingShapes = true;

    /*FIXME This implementation is very evil! At first, all shapes are created on
          canvas. In a second step, the attributes are applied. There must be a distinction
          between the configuration phase (where the outgoings, for example, are just named),
          and the creation phase (where the outgoings are evaluated). This must be reflected
          in code to provide a nicer API/ implementation!!! */

    var addShape = function (shape, parent) {
      // Create a new Stencil
      var stencil = ORYX.Core.StencilSet.stencil(this.getStencil().namespace() + shape.stencil.id);

      // Create a new Shape
      var ShapeClass = stencil.type() == 'node' ? ORYX.Core.Node : ORYX.Core.Edge;
      var newShape = new ShapeClass({ eventHandlerCallback: eventHandler }, stencil, this.facade);

      // Set the resource id
      newShape.resourceId = shape.resourceId;
      newShape.node.id = 'svg-' + shape.resourceId;

      // Set parent to json object to be used later
      // Due to the nested json structure, normally shape.parent is not set/ must not be set.
      // In special cases, it can be easier to set this directly instead of a nested structure.
      shape.parent = '#' + ((shape.parent && shape.parent.resourceId) || parent.resourceId);

      // Add the shape to the canvas
      this.add(newShape);

      return {
        json: shape,
        object: newShape,
      };
    }.bind(this);

    /** Builds up recursively a flatted array of shapes, including a javascript object and json representation
     * @param {Object} shape Any object that has Object#childShapes
     */
    var addChildShapesRecursively = function (shape) {
      var addedShapes = [];

      if (shape.childShapes && shape.childShapes.constructor == String) {
        shape.childShapes = JSON.parse(shape.childShapes);
      }

      shape.childShapes.each(function (childShape) {
        addedShapes.push(addShape(childShape, shape));
        addedShapes = addedShapes.concat(addChildShapesRecursively(childShape));
      });

      return addedShapes;
    }.bind(this);

    var shapes = addChildShapesRecursively({
      childShapes: shapeObjects,
      resourceId: this.resourceId,
    });

    // prepare deserialisation parameter
    shapes.each(
      function (shape) {
        var properties = [];
        for (field in shape.json.properties) {
          properties.push({
            prefix: 'oryx',
            name: field,
            value: shape.json.properties[field],
          });
        }

        // Outgoings
        shape.json.outgoing.each(function (out) {
          properties.push({
            prefix: 'raziel',
            name: 'outgoing',
            value: '#' + out.resourceId,
          });
        });

        // Target
        // (because of a bug, the first outgoing is taken when there is no target,
        // can be removed after some time)
        if (shape.object instanceof ORYX.Core.Edge) {
          var target = shape.json.target || shape.json.outgoing[0];
          if (target) {
            properties.push({
              prefix: 'raziel',
              name: 'target',
              value: '#' + target.resourceId,
            });
          }
        }

        // Bounds
        if (shape.json.bounds) {
          properties.push({
            prefix: 'oryx',
            name: 'bounds',
            value:
              shape.json.bounds.upperLeft.x +
              ',' +
              shape.json.bounds.upperLeft.y +
              ',' +
              shape.json.bounds.lowerRight.x +
              ',' +
              shape.json.bounds.lowerRight.y,
          });
        }

        //Dockers [{x:40, y:50}, {x:30, y:60}] => "40 50 30 60  #"
        if (shape.json.dockers) {
          properties.push({
            prefix: 'oryx',
            name: 'dockers',
            value:
              shape.json.dockers.inject('', function (dockersStr, docker) {
                return dockersStr + docker.x + ' ' + docker.y + ' ';
              }) + ' #',
          });
        }

        //Parent
        properties.push({
          prefix: 'raziel',
          name: 'parent',
          value: shape.json.parent,
        });

        shape.__properties = properties;
      }.bind(this)
    );

    // Deserialize the properties from the shapes
    // This can't be done earlier because Shape#deserialize expects that all referenced nodes are already there

    // first, deserialize all nodes
    shapes.each(function (shape) {
      if (shape.object instanceof ORYX.Core.Node) {
        shape.object.deserialize(shape.__properties, shape.json);
      }
    });

    // second, deserialize all edges
    shapes.each(function (shape) {
      if (shape.object instanceof ORYX.Core.Edge) {
        shape.object.deserialize(shape.__properties, shape.json);
        shape.object._oldBounds = shape.object.bounds.clone();
        shape.object._update();
      }
    });

    delete this.initializingShapes;
    return shapes.pluck('object');
  },

  /**
   * Updates the size of the canvas, regarding to the containg shapes.
   */
  updateSize: function () {
    // Check the size for the canvas
    var maxWidth = 0;
    var maxHeight = 0;
    var offset = 100;
    this.getChildShapes(true, function (shape) {
      var b = shape.bounds;
      maxWidth = Math.max(maxWidth, b.lowerRight().x + offset);
      maxHeight = Math.max(maxHeight, b.lowerRight().y + offset);
    });

    if (this.bounds.width() < maxWidth || this.bounds.height() < maxHeight) {
      this.setSize({ width: Math.max(this.bounds.width(), maxWidth), height: Math.max(this.bounds.height(), maxHeight) });
    }
  },

  getRootNode: function () {
    return this.rootNode;
  },

  getUnderlayNode: function () {
    return this.underlayNode;
  },

  getSvgContainer: function () {
    return this.node.childNodes[1];
  },

  getHTMLContainer: function () {
    return this._htmlContainer;
  },

  /**
   * Return all elements of the same highest level
   * @param {Object} elements
   */
  getShapesWithSharedParent: function (elements) {
    // If there is no elements, return []
    if (!elements || elements.length < 1) {
      return [];
    }
    // If there is one element, return this element
    if (elements.length == 1) {
      return elements;
    }

    return elements.findAll(function (value) {
      var parentShape = value.parent;
      while (parentShape) {
        if (elements.member(parentShape)) return false;
        parentShape = parentShape.parent;
      }
      return true;
    });
  },

  setSize: function (size, dontSetBounds) {
    if (!size || !size.width || !size.height) {
      return;
    }

    if (this.rootNode.parentNode) {
      this.rootNode.parentNode.style.width = size.width + 'px';
      this.rootNode.parentNode.style.height = size.height + 'px';
    }

    this.rootNode.setAttributeNS(null, 'width', size.width);
    this.rootNode.setAttributeNS(null, 'height', size.height);

    //this._htmlContainer.style.top = "-" + (size.height + 4) + 'px';
    if (!dontSetBounds) {
      this.bounds.set({ a: { x: 0, y: 0 }, b: { x: size.width / this.zoomLevel, y: size.height / this.zoomLevel } });
    }
  },

  /**
   * Returns an SVG document of the current process.
   * @param {Boolean} escapeText Use true, if you want to parse it with an XmlParser,
   * 					false, if you want to use the SVG document in browser on client side.
   */
  getSVGRepresentation: function (escapeText) {
    // Get the serialized svg image source
    var svgClone = this.getRootNode().cloneNode(true);

    this._removeInvisibleElements(svgClone);

    var x1, y1, x2, y2;
    this.getChildShapes(true).each(function (shape) {
      var absBounds = shape.absoluteBounds();
      var ul = absBounds.upperLeft();
      var lr = absBounds.lowerRight();
      if (x1 == undefined) {
        x1 = ul.x;
        y1 = ul.y;
        x2 = lr.x;
        y2 = lr.y;
      } else {
        x1 = Math.min(x1, ul.x);
        y1 = Math.min(y1, ul.y);
        x2 = Math.max(x2, lr.x);
        y2 = Math.max(y2, lr.y);
      }
    });

    var margin = 50;
    var width, height, tx, ty;
    if (x1 == undefined) {
      width = 0;
      height = 0;
      tx = 0;
      ty = 0;
    } else {
      width = x2;
      height = y2;
      tx = -x1 + margin / 2;
      ty = -y1 + margin / 2;
    }

    // Set the width and height
    svgClone.setAttributeNS(null, 'width', width + margin);
    svgClone.setAttributeNS(null, 'height', height + margin);

    //remove scale factor
    svgClone.childNodes[1].removeAttributeNS(null, 'transform');

    try {
      var svgCont = svgClone.childNodes[1].childNodes[1];
      svgCont.parentNode.removeChild(svgCont);
    } catch (e) {}

    if (escapeText) {
      $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')).each(function (elem) {
        elem.textContent = elem.textContent.escapeHTML();
      });

      $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text')).each(function (elem) {
        if (elem.childNodes.length == 0) elem.textContent = elem.textContent.escapeHTML();
      });
    }

    // generating absolute urls for the pdf-exporter
    $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'image')).each(function (elem) {
      var href = elem.getAttributeNS('http://www.w3.org/1999/xlink', 'href');

      if (!href.match('^(http|https)://')) {
        href = window.location.protocol + '//' + window.location.host + href;
        elem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', href);
      }
    });

    // escape all links
    $A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'a')).each(function (elem) {
      elem.setAttributeNS(
        'http://www.w3.org/1999/xlink',
        'xlink:href',
        (elem.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || '').escapeHTML()
      );
    });

    return svgClone;
  },

  /**
   * Removes all nodes (and its children) that has the
   * attribute visibility set to "hidden"
   */
  _removeInvisibleElements: function (element) {
    var index = 0;
    while (index < element.childNodes.length) {
      var child = element.childNodes[index];
      if (child.getAttributeNS && child.getAttributeNS(null, 'visibility') === 'hidden') {
        element.removeChild(child);
      } else {
        this._removeInvisibleElements(child);
        index++;
      }
    }
  },

  /**
   * This method checks all shapes on the canvas and removes all shapes that
   * contain invalid bounds values or dockers values(NaN)
   */
  /*cleanUp: function(parent) {
		if (!parent) {
			parent = this;
		}
		parent.getChildShapes().each(function(shape){
			var a = shape.bounds.a;
			var b = shape.bounds.b;
			if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
				parent.remove(shape);
			}
			else {
				shape.getDockers().any(function(docker) {
					a = docker.bounds.a;
					b = docker.bounds.b;
					if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
						parent.remove(shape);
						return true;
					}
					return false;
				});
				shape.getMagnets().any(function(magnet) {
					a = magnet.bounds.a;
					b = magnet.bounds.b;
					if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) {
						parent.remove(shape);
						return true;
					}
					return false;
				});
				this.cleanUp(shape);
			}
		}.bind(this));
	},*/

  _delegateEvent: function (event) {
    if (this.eventHandlerCallback && (event.target == this.rootNode || event.target == this.rootNode.parentNode)) {
      this.eventHandlerCallback(event, this);
    }
  },

  toString: function () {
    return 'Canvas ' + this.id;
  },

  /**
   * Calls {@link ORYX.Core.AbstractShape#toJSON} and adds some stencil set information.
   */
  toJSON: function () {
    var json = arguments.callee.$.toJSON.apply(this, arguments);

    //		if(ORYX.CONFIG.STENCILSET_HANDLER.length > 0) {
    //			json.stencilset = {
    //				url: this.getStencil().stencilSet().namespace()
    //	        };
    //		} else {
    json.stencilset = {
      url: this.getStencil().stencilSet().source(),
      namespace: this.getStencil().stencilSet().namespace(),
    };
    //		}

    return json;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
var idCounter = 0;
var ID_PREFIX = 'resource';

/**
 * Main initialization method. To be called when loading
 * of the document, including all scripts, is completed.
 */
function init() {
  ORYX.Log.debug('Querying editor instances');

  // Hack for WebKit to set the SVGElement-Classes
  ORYX.Editor.setMissingClasses();

  // If someone wants to create the editor instance himself
  if (window.onOryxResourcesLoaded) {
    window.onOryxResourcesLoaded();
  }
  // Else fetch the model from server and display editor
  else {
    var modelId = window.location.search.substring(4);
    var modelUrl = './service/model/' + modelId + '/json';

    ORYX.Editor.createByUrl(modelUrl);
  }
}

/**
   @namespace Global Oryx name space
   @name ORYX
*/
if (!ORYX) {
  var ORYX = {};
}

/**
 * The Editor class.
 * @class ORYX.Editor
 * @extends Clazz
 * @param {Object} config An editor object, passed to {@link ORYX.Editor#loadSerialized}
 * @param {String} config.id Any ID that can be used inside the editor. If fullscreen=false, any HTML node with this id must be present to render the editor to this node.
 * @param {boolean} [config.fullscreen=true] Render editor in fullscreen mode or not.
 * @param {String} config.stencilset.url Stencil set URL.
 * @param {String} [config.stencil.id] Stencil type used for creating the canvas.
 * @param {Object} config.properties Any properties applied to the canvas.
 */
ORYX.Editor = {
  /** @lends ORYX.Editor.prototype */
  // Defines the global dom event listener
  DOMEventListeners: new Hash(),

  // Defines the selection
  selection: [],

  // Defines the current zoom level
  zoomLevel: 1.0,

  construct: function (config) {
    // initialization.
    this._eventsQueue = [];
    this.loadedPlugins = [];
    this.pluginsData = [];

    //meta data about the model for the signavio warehouse
    //directory, new, name, description, revision, model (the model data)

    this.modelMetaData = config;

    var model = config;

    this.id = model.modelId;

    if (config.model) {
      model = config.model;
    }

    if (!this.id) {
      this.id = model.id;
      if (!this.id) {
        this.id = ORYX.Editor.provideId();
      }
    }

    // Defines if the editor should be fullscreen or not
    this.fullscreen = config.fullscreen !== false;

    // Initialize the eventlistener
    this._initEventListener();

    // Load particular stencilset
    if (ORYX.CONFIG.BACKEND_SWITCH) {
      var ssUrl = (model.stencilset.namespace || model.stencilset.url).replace('#', '%23');
      ORYX.Core.StencilSet.loadStencilSet(ssUrl, this.modelMetaData, this.id);
    } else {
      var ssUrl = model.stencilset.url;
      ORYX.Core.StencilSet.loadStencilSet(ssUrl, this.modelMetaData, this.id);
    }

    // CREATES the canvas
    this._createCanvas(model.stencil ? model.stencil.id : null, model.properties);

    // GENERATES the whole EXT.VIEWPORT
    this._generateGUI();

    // Initializing of a callback to check loading ends
    var loadPluginFinished = false;
    var loadContentFinished = false;
    var initFinished = function () {
      if (!loadPluginFinished || !loadContentFinished) {
        return;
      }
      this._finishedLoading();
    }.bind(this);

    // LOAD the plugins
    window.setTimeout(
      function () {
        this.loadPlugins();
        loadPluginFinished = true;
        initFinished();
      }.bind(this),
      100
    );

    // LOAD the content of the current editor instance
    window.setTimeout(
      function () {
        this.loadSerialized(model, true); // Request the meta data as well
        this.getCanvas().update();
        loadContentFinished = true;
        initFinished();
      }.bind(this),
      200
    );
  },

  _finishedLoading: function () {
    // Raise Loaded Event
    this.handleEvents({ type: ORYX.CONFIG.EVENT_LOADED });
  },

  _initEventListener: function () {
    // Register on Events

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYDOWN, this.catchKeyDownEvents.bind(this), false);
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYUP, this.catchKeyUpEvents.bind(this), false);

    // Enable Key up and down Event
    this._keydownEnabled = true;
    this._keyupEnabled = true;

    this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEDOWN] = [];
    this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEUP] = [];
    this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOVER] = [];
    this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOUT] = [];
    this.DOMEventListeners[ORYX.CONFIG.EVENT_SELECTION_CHANGED] = [];
    this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEMOVE] = [];
  },

  /**
   * Generate the whole viewport of the
   * Editor and initialized the Ext-Framework
   *
   */
  _generateGUI: function () {
    // Defines the layout height if it's NOT fullscreen
    var layoutHeight = ORYX.CONFIG.WINDOW_HEIGHT;
    var canvasParent = this.getCanvas().rootNode.parentNode;

    jQuery('#canvasSection').append(canvasParent);

    // Set the editor to the center, and refresh the size
    canvasParent.parentNode.setAttributeNS(null, 'align', 'center');
    canvasParent.setAttributeNS(null, 'align', 'left');
    this.getCanvas().setSize({
      width: ORYX.CONFIG.CANVAS_WIDTH,
      height: ORYX.CONFIG.CANVAS_HEIGHT,
    });
  },

  getAvailablePlugins: function () {
    var curAvailablePlugins = ORYX.availablePlugins.clone();
    curAvailablePlugins.each(
      function (plugin) {
        if (
          this.loadedPlugins.find(
            function (loadedPlugin) {
              return loadedPlugin.type == this.name;
            }.bind(plugin)
          )
        ) {
          plugin.engaged = true;
        } else {
          plugin.engaged = false;
        }
      }.bind(this)
    );
    return curAvailablePlugins;
  },

  loadScript: function (url, callback) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    if (script.readyState) {
      //IE
      script.onreadystatechange = function () {
        if (script.readyState == 'loaded' || script.readyState == 'complete') {
          script.onreadystatechange = null;
          callback();
        }
      };
    } else {
      //Others
      script.onload = function () {
        callback();
      };
    }
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  },
  /**
   * activate Plugin
   *
   * @param {String} name
   * @param {Function} callback
   * 		callback(sucess, [errorCode])
   * 			errorCodes: NOTUSEINSTENCILSET, REQUIRESTENCILSET, NOTFOUND, YETACTIVATED
   */
  activatePluginByName: function (name, callback, loadTry) {
    var match = this.getAvailablePlugins().find(function (value) {
      return value.name == name;
    });
    if (match && (!match.engaged || match.engaged === 'false')) {
      var loadedStencilSetsNamespaces = this.getStencilSets().keys();
      var facade = this._getPluginFacade();
      var newPlugin;
      var me = this;
      ORYX.Log.debug("Initializing plugin '%0'", match.name);

      if (
        !match.requires ||
        !match.requires.namespaces ||
        match.requires.namespaces.any(function (req) {
          return loadedStencilSetsNamespaces.indexOf(req) >= 0;
        })
      ) {
        if (
          !match.notUsesIn ||
          !match.notUsesIn.namespaces ||
          !match.notUsesIn.namespaces.any(function (req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0;
          })
        ) {
          try {
            var className = eval(match.name);
            var newPlugin = new className(facade, match);
            newPlugin.type = match.name;

            // If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
            if (newPlugin.registryChanged) newPlugin.registryChanged(me.pluginsData);

            // If there have an onSelection-Method it will pushed to the Editor Event-Handler
            if (newPlugin.onSelectionChanged)
              me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, newPlugin.onSelectionChanged.bind(newPlugin));
            this.loadedPlugins.push(newPlugin);
            this.loadedPlugins.each(
              function (loaded) {
                if (loaded.registryChanged) loaded.registryChanged(this.pluginsData);
              }.bind(me)
            );
            callback(true);
          } catch (e) {
            ORYX.Log.warn('Plugin %0 is not available', match.name);
            if (!!loadTry) {
              callback(false, 'INITFAILED');
              return;
            }
            this.loadScript('plugins/scripts/' + match.source, this.activatePluginByName.bind(this, match.name, callback, true));
          }
        } else {
          callback(false, 'NOTUSEINSTENCILSET');
          ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name);
        }
      } else {
        callback(false, 'REQUIRESTENCILSET');
        ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name);
      }
    } else {
      callback(false, match ? 'NOTFOUND' : 'YETACTIVATED');
      //TODO error handling
    }
  },

  /**
   *  Laden der Plugins
   */
  loadPlugins: function () {
    // if there should be plugins but still are none, try again.
    // TODO this should wait for every plugin respectively.
    /*if (!ORYX.Plugins && ORYX.availablePlugins.length > 0) {
			window.setTimeout(this.loadPlugins.bind(this), 100);
			return;
		}*/

    var me = this;
    var newPlugins = [];

    var loadedStencilSetsNamespaces = this.getStencilSets().keys();

    // Available Plugins will be initalize
    var facade = this._getPluginFacade();

    // If there is an Array where all plugins are described, than only take those
    // (that comes from the usage of oryx with a mashup api)
    if (ORYX.MashupAPI && ORYX.MashupAPI.loadablePlugins && ORYX.MashupAPI.loadablePlugins instanceof Array) {
      // Get the plugins from the available plugins (those who are in the plugins.xml)
      ORYX.availablePlugins = $A(ORYX.availablePlugins).findAll(function (value) {
        return ORYX.MashupAPI.loadablePlugins.include(value.name);
      });

      // Add those plugins to the list, which are only in the loadablePlugins list
      ORYX.MashupAPI.loadablePlugins.each(function (className) {
        if (
          !ORYX.availablePlugins.find(function (val) {
            return val.name == className;
          })
        ) {
          ORYX.availablePlugins.push({ name: className });
        }
      });
    }

    ORYX.availablePlugins.each(function (value) {
      ORYX.Log.debug("Initializing plugin '%0'", value.name);
      if (
        (!value.requires ||
          !value.requires.namespaces ||
          value.requires.namespaces.any(function (req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0;
          })) &&
        (!value.notUsesIn ||
          !value.notUsesIn.namespaces ||
          !value.notUsesIn.namespaces.any(function (req) {
            return loadedStencilSetsNamespaces.indexOf(req) >= 0;
          })) &&
        /*only load activated plugins or undefined */
        (value.engaged || value.engaged === undefined)
      ) {
        try {
          var className = eval(value.name);
          if (className) {
            var plugin = new className(facade, value);
            plugin.type = value.name;
            newPlugins.push(plugin);
            plugin.engaged = true;
          }
        } catch (e) {
          ORYX.Log.warn('Plugin %0 is not available %1', value.name, e);
        }
      } else {
        ORYX.Log.info("Plugin need a stencilset which is not loaded'", value.name);
      }
    });

    newPlugins.each(function (value) {
      // If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
      if (value.registryChanged) value.registryChanged(me.pluginsData);

      // If there have an onSelection-Method it will pushed to the Editor Event-Handler
      if (value.onSelectionChanged) me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, value.onSelectionChanged.bind(value));
    });

    this.loadedPlugins = newPlugins;

    this.registerPluginsOnKeyEvents();

    this.setSelection();
  },

  /**
   * Creates the Canvas
   * @param {String} [stencilType] The stencil type used for creating the canvas. If not given, a stencil with myBeRoot = true from current stencil set is taken.
   * @param {Object} [canvasConfig] Any canvas properties (like language).
   */
  _createCanvas: function (stencilType, canvasConfig) {
    if (stencilType) {
      // Add namespace to stencilType
      if (stencilType.search(/^http/) === -1) {
        stencilType = this.getStencilSets().values()[0].namespace() + stencilType;
      }
    } else {
      // Get any root stencil type
      stencilType = this.getStencilSets().values()[0].findRootStencilName();
    }

    // get the stencil associated with the type
    var canvasStencil = ORYX.Core.StencilSet.stencil(stencilType);

    if (!canvasStencil)
      ORYX.Log.fatal(
        'Initialisation failed, because the stencil with the type %0 is not part of one of the loaded stencil sets.',
        stencilType
      );

    // create all dom
    // TODO fix border, so the visible canvas has a double border and some spacing to the scrollbars
    var div = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', null, ['div']);
    // set class for custom styling
    div.addClassName('ORYX_Editor');

    // create the canvas
    this._canvas = new ORYX.Core.Canvas(
      {
        width: ORYX.CONFIG.CANVAS_WIDTH,
        height: ORYX.CONFIG.CANVAS_HEIGHT,
        eventHandlerCallback: this.handleEvents.bind(this),
        id: this.id,
        parentNode: div,
      },
      canvasStencil,
      this._getPluginFacade()
    );

    if (canvasConfig) {
      // Migrate canvasConfig to an RDF-like structure
      //FIXME this isn't nice at all because we don't want rdf any longer
      var properties = [];
      for (field in canvasConfig) {
        properties.push({
          prefix: 'oryx',
          name: field,
          value: canvasConfig[field],
        });
      }

      this._canvas.deserialize(properties);
    }
  },

  /**
   * Returns a per-editor singleton plugin facade.
   * To be used in plugin initialization.
   */
  _getPluginFacade: function () {
    // if there is no pluginfacade already created:
    if (!this._pluginFacade)
      // create it.
      this._pluginFacade = {
        activatePluginByName: this.activatePluginByName.bind(this),
        //deactivatePluginByName:		this.deactivatePluginByName.bind(this),
        getAvailablePlugins: this.getAvailablePlugins.bind(this),
        offer: this.offer.bind(this),
        getStencilSets: this.getStencilSets.bind(this),
        getStencilSetExtensionDefinition: function () {
          return Object.clone(this.ss_extensions_def || {});
        }.bind(this),
        getRules: this.getRules.bind(this),
        loadStencilSet: this.loadStencilSet.bind(this),
        createShape: this.createShape.bind(this),
        deleteShape: this.deleteShape.bind(this),
        getSelection: this.getSelection.bind(this),
        setSelection: this.setSelection.bind(this),
        updateSelection: this.updateSelection.bind(this),
        getCanvas: this.getCanvas.bind(this),

        importJSON: this.importJSON.bind(this),
        getJSON: this.getJSON.bind(this),
        getSerializedJSON: this.getSerializedJSON.bind(this),

        executeCommands: this.executeCommands.bind(this),
        isExecutingCommands: this.isExecutingCommands.bind(this),

        registerOnEvent: this.registerOnEvent.bind(this),
        unregisterOnEvent: this.unregisterOnEvent.bind(this),
        raiseEvent: this.handleEvents.bind(this),
        enableEvent: this.enableEvent.bind(this),
        disableEvent: this.disableEvent.bind(this),

        eventCoordinates: this.eventCoordinates.bind(this),
        eventCoordinatesXY: this.eventCoordinatesXY.bind(this),

        getModelMetaData: this.getModelMetaData.bind(this),
      };

    // return it.
    return this._pluginFacade;
  },

  isExecutingCommands: function () {
    return !!this.commandExecuting;
  },

  /**
   * Implementes the command pattern
   * (The real usage of the command pattern
   * is implemented and shown in the Plugins/undo.js)
   *
   * @param <Oryx.Core.Command>[] Array of commands
   */
  executeCommands: function (commands) {
    if (!this.commandStack) {
      this.commandStack = [];
    }
    if (!this.commandStackExecuted) {
      this.commandStackExecuted = [];
    }

    this.commandStack = [].concat(this.commandStack).concat(commands);

    // Check if already executes
    if (this.commandExecuting) {
      return;
    }

    // Start execution
    this.commandExecuting = true;

    // Iterate over all commands
    while (this.commandStack.length > 0) {
      var command = this.commandStack.shift();
      // and execute it
      command.execute();
      this.commandStackExecuted.push(command);
    }

    // Raise event for executing commands
    this.handleEvents({
      type: ORYX.CONFIG.EVENT_EXECUTE_COMMANDS,
      commands: this.commandStackExecuted,
    });

    // Remove temporary vars
    delete this.commandStack;
    delete this.commandStackExecuted;
    delete this.commandExecuting;

    this.updateSelection();
  },

  /**
   * Returns JSON of underlying canvas (calls ORYX.Canvas#toJSON()).
   * @return {Object} Returns JSON representation as JSON object.
   */
  getJSON: function () {
    delete Array.prototype.toJSON;
    var canvasJSON = this.getCanvas().toJSON();
    canvasJSON.ssextensions = this.getStencilSets()
      .values()[0]
      .extensions()
      .keys()
      .findAll(function (sse) {
        return !sse.endsWith('/meta#');
      });
    return canvasJSON;
  },

  /**
   * Serializes a call to toJSON().
   * @return {String} Returns JSON representation as string.
   */
  getSerializedJSON: function () {
    return JSON.stringify(this.getJSON());
  },

  /**
   * Imports shapes in JSON as expected by {@link ORYX.Editor#loadSerialized}
   * @param {Object|String} jsonObject The (serialized) json object to be imported
   * @param {boolean } [noSelectionAfterImport=false] Set to true if no shapes should be selected after import
   * @throws {SyntaxError} If the serialized json object contains syntax errors
   */
  importJSON: function (jsonObject, noSelectionAfterImport) {
    try {
      jsonObject = this.renewResourceIds(jsonObject);
    } catch (error) {
      throw error;
    }
    //check, if the imported json model can be loaded in this editor
    // (stencil set has to fit)
    if (jsonObject.stencilset.namespace && jsonObject.stencilset.namespace !== this.getCanvas().getStencil().stencilSet().namespace()) {
      alert(
        String.format(ORYX.I18N.JSONImport.wrongSS, jsonObject.stencilset.namespace, this.getCanvas().getStencil().stencilSet().namespace())
      );
      return null;
    } else {
      var commandClass = ORYX.Core.Command.extend({
        construct: function (jsonObject, loadSerializedCB, noSelectionAfterImport, facade) {
          this.jsonObject = jsonObject;
          this.noSelection = noSelectionAfterImport;
          this.facade = facade;
          this.shapes;
          this.connections = [];
          this.parents = new Hash();
          this.selection = this.facade.getSelection();
          this.loadSerialized = loadSerializedCB;
        },
        execute: function () {
          if (!this.shapes) {
            // Import the shapes out of the serialization
            this.shapes = this.loadSerialized(this.jsonObject);

            //store all connections
            this.shapes.each(
              function (shape) {
                if (shape.getDockers) {
                  var dockers = shape.getDockers();
                  if (dockers) {
                    if (dockers.length > 0) {
                      this.connections.push([dockers.first(), dockers.first().getDockedShape(), dockers.first().referencePoint]);
                    }
                    if (dockers.length > 1) {
                      this.connections.push([dockers.last(), dockers.last().getDockedShape(), dockers.last().referencePoint]);
                    }
                  }
                }

                //store parents
                this.parents[shape.id] = shape.parent;
              }.bind(this)
            );
          } else {
            this.shapes.each(
              function (shape) {
                this.parents[shape.id].add(shape);
              }.bind(this)
            );

            this.connections.each(function (con) {
              con[0].setDockedShape(con[1]);
              con[0].setReferencePoint(con[2]);
              con[0].update();
            });
          }

          //this.parents.values().uniq().invoke("update");
          this.facade.getCanvas().update();

          if (!this.noSelection) this.facade.setSelection(this.shapes);
          else this.facade.updateSelection();

          // call updateSize again, because during loadSerialized the edges' bounds
          // are not yet initialized properly
          this.facade.getCanvas().updateSize();
        },
        rollback: function () {
          var selection = this.facade.getSelection();

          this.shapes.each(
            function (shape) {
              selection = selection.without(shape);
              this.facade.deleteShape(shape);
            }.bind(this)
          );

          /*this.parents.values().uniq().each(function(parent) {
						if(!this.shapes.member(parent))
							parent.update();
					}.bind(this));*/

          this.facade.getCanvas().update();

          this.facade.setSelection(selection);
        },
      });

      var command = new commandClass(jsonObject, this.loadSerialized.bind(this), noSelectionAfterImport, this._getPluginFacade());

      this.executeCommands([command]);

      return command.shapes.clone();
    }
  },

  /**
   * This method renew all resource Ids and according references.
   * Warning: The implementation performs a substitution on the serialized object for
   * easier implementation. This results in a low performance which is acceptable if this
   * is only used when importing models.
   * @param {Object|String} jsonObject
   * @throws {SyntaxError} If the serialized json object contains syntax errors.
   * @return {Object} The jsonObject with renewed ids.
   * @private
   */
  renewResourceIds: function (jsonObject) {
    // For renewing resource ids, a serialized and object version is needed
    if (Object.prototype.toString.call(jsonObject) === 'String') {
      try {
        var serJsonObject = jsonObject;
        jsonObject = JSON.parse(jsonObject);
      } catch (error) {
        throw new SyntaxError(error.message);
      }
    } else {
      var serJsonObject = JSON.stringify(jsonObject);
    }

    // collect all resourceIds recursively
    var collectResourceIds = function (shapes) {
      if (!shapes) return [];

      return shapes
        .map(function (shape) {
          return collectResourceIds(shape.childShapes).concat(shape.resourceId);
        })
        .flatten();
    };
    var resourceIds = collectResourceIds(jsonObject.childShapes);

    // Replace each resource id by a new one
    resourceIds.each(function (oldResourceId) {
      var newResourceId = ORYX.Editor.provideId();
      serJsonObject = serJsonObject.replace(new RegExp(oldResourceId, 'g'), newResourceId);
    });

    return JSON.parse(serJsonObject);
  },

  /**
   * Loads serialized model to the oryx.
   * @example
   * editor.loadSerialized({
   *    resourceId: "mymodel1",
   *    childShapes: [
   *       {
   *          stencil:{ id:"Subprocess" },
   *          outgoing:[{resourceId: 'aShape'}],
   *          target: {resourceId: 'aShape'},
   *          bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } },
   *          resourceId: "myshape1",
   *          childShapes:[],
   *          properties:{},
   *       }
   *    ],
   *    properties:{
   *       language: "English"
   *    },
   *    stencilset:{
   *       url:"http://localhost:8080/oryx/stencilsets/bpmn1.1/bpmn1.1.json"
   *    },
   *    stencil:{
   *       id:"BPMNDiagram"
   *    }
   * });
   * @param {Object} model Description of the model to load.
   * @param {Array} [model.ssextensions] List of stenctil set extensions.
   * @param {String} model.stencilset.url
   * @param {String} model.stencil.id
   * @param {Array} model.childShapes
   * @param {Array} [model.properties]
   * @param {String} model.resourceId
   * @return {ORYX.Core.Shape[]} List of created shapes
   * @methodOf ORYX.Editor.prototype
   */
  loadSerialized: function (model, requestMeta) {
    var canvas = this.getCanvas();

    // Bugfix (cf. http://code.google.com/p/oryx-editor/issues/detail?id=240)
    // Deserialize the canvas' stencil set extensions properties first!
    this.loadSSExtensions(model.ssextensions);

    // Load Meta Data Extension if available
    // #Signavio
    if (requestMeta === true) {
      var metaDataExtension = this.getExtensionForMetaData();
      if (metaDataExtension) {
        this.loadSSExtension(metaDataExtension);
      }
    }

    var shapes = this.getCanvas().addShapeObjects(model.childShapes, this.handleEvents.bind(this));

    if (model.properties) {
      for (key in model.properties) {
        var value = model.properties[key];
        var prop = this.getCanvas()
          .getStencil()
          .property('oryx-' + key);
        if (!(typeof value === 'string') && (!prop || !prop.isList())) {
          value = JSON.stringify(value);
        }
        this.getCanvas().setProperty('oryx-' + key, value);
      }
    }

    this.getCanvas().updateSize();

    // Force to update the selection
    this.selection = [null];
    this.setSelection([]);

    return shapes;
  },

  /**
   * Return the namespace of the extension which
   * provided all the self defined meta data
   * @return {String} Returns null if no extension is defined, otherwise the namespace
   *
   */
  getExtensionForMetaData: function () {
    if (!this.ss_extensions_def || !(this.ss_extensions_def.extensions instanceof Array)) {
      return null;
    }

    var stencilsets = this.getStencilSets();
    var extension = this.ss_extensions_def.extensions.find(function (ex) {
      return !!stencilsets[ex['extends']] && ex.namespace.endsWith('/meta#');
    });

    return extension ? extension.namespace || null : null;
  },

  /**
   * Calls ORYX.Editor.prototype.ss_extension_namespace for each element
   * @param {Array} ss_extension_namespaces An array of stencil set extension namespaces.
   */
  loadSSExtensions: function (ss_extension_namespaces) {
    if (!ss_extension_namespaces) return;

    ss_extension_namespaces.each(
      function (ss_extension_namespace) {
        this.loadSSExtension(ss_extension_namespace);
      }.bind(this)
    );
  },

  /**
   * Loads a stencil set extension.
   * The stencil set extensions definiton file must already
   * be loaded when the editor is initialized.
   */
  loadSSExtension: function (ss_extension_namespace) {
    if (this.ss_extensions_def) {
      var extension = this.ss_extensions_def.extensions.find(function (ex) {
        return ex.namespace == ss_extension_namespace;
      });

      if (!extension) {
        return;
      }

      var stencilset = this.getStencilSets()[extension['extends']];

      if (!stencilset) {
        return;
      }

      // Check if absolute or relative url
      if ((extension['definition'] || '').startsWith('/')) {
        stencilset.addExtension(extension['definition']);
      } else {
        stencilset.addExtension(ORYX.CONFIG.SS_EXTENSIONS_FOLDER + extension['definition']);
      }

      //stencilset.addExtension("/oryx/build/stencilsets/extensions/" + extension["definition"])
      this.getRules().initializeRules(stencilset);

      this._getPluginFacade().raiseEvent({
        type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED,
      });
    }
  },

  disableEvent: function (eventType) {
    if (eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
      this._keydownEnabled = false;
    }
    if (eventType == ORYX.CONFIG.EVENT_KEYUP) {
      this._keyupEnabled = false;
    }
    if (this.DOMEventListeners.keys().member(eventType)) {
      var value = this.DOMEventListeners.remove(eventType);
      this.DOMEventListeners['disable_' + eventType] = value;
    }
  },

  enableEvent: function (eventType) {
    if (eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
      this._keydownEnabled = true;
    }

    if (eventType == ORYX.CONFIG.EVENT_KEYUP) {
      this._keyupEnabled = true;
    }

    if (this.DOMEventListeners.keys().member('disable_' + eventType)) {
      var value = this.DOMEventListeners.remove('disable_' + eventType);
      this.DOMEventListeners[eventType] = value;
    }
  },

  /**
   *  Methods for the PluginFacade
   */
  registerOnEvent: function (eventType, callback) {
    if (!this.DOMEventListeners.keys().member(eventType)) {
      this.DOMEventListeners[eventType] = [];
    }

    this.DOMEventListeners[eventType].push(callback);
  },

  unregisterOnEvent: function (eventType, callback) {
    if (this.DOMEventListeners.keys().member(eventType)) {
      this.DOMEventListeners[eventType] = this.DOMEventListeners[eventType].without(callback);
    } else {
      // Event is not supported
      // TODO: Error Handling
    }
  },

  getSelection: function () {
    return this.selection || [];
  },

  getStencilSets: function () {
    return ORYX.Core.StencilSet.stencilSets(this.id);
  },

  getRules: function () {
    return ORYX.Core.StencilSet.rules(this.id);
  },

  loadStencilSet: function (source) {
    try {
      ORYX.Core.StencilSet.loadStencilSet(source, this.modelMetaData, this.id);
      this.handleEvents({ type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED });
    } catch (e) {
      ORYX.Log.warn('Requesting stencil set file failed. (' + e + ')');
    }
  },

  offer: function (pluginData) {
    if (!this.pluginsData.member(pluginData)) {
      this.pluginsData.push(pluginData);
    }
  },

  /**
   * It creates an new event or adds the callback, if already existing,
   * for the key combination that the plugin passes in keyCodes attribute
   * of the offer method.
   *
   * The new key down event fits the schema:
   * 		key.event[.metactrl][.alt][.shift].'thekeyCode'
   */
  registerPluginsOnKeyEvents: function () {
    this.pluginsData.each(
      function (pluginData) {
        if (pluginData.keyCodes) {
          pluginData.keyCodes.each(
            function (keyComb) {
              var eventName = 'key.event';

              /* Include key action */
              eventName += '.' + keyComb.keyAction;

              if (keyComb.metaKeys) {
                /* Register on ctrl or apple meta key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_META_CTRL) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_META_CTRL;
                }

                /* Register on alt key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_ALT) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_ALT;
                }

                /* Register on shift key as meta key */
                if (keyComb.metaKeys.indexOf(ORYX.CONFIG.META_KEY_SHIFT) > -1) {
                  eventName += '.' + ORYX.CONFIG.META_KEY_SHIFT;
                }
              }

              /* Register on the actual key */
              if (keyComb.keyCode) {
                eventName += '.' + keyComb.keyCode;
              }

              /* Register the event */
              ORYX.Log.debug('Register Plugin on Key Event: %0', eventName);
              if (pluginData.toggle === true && pluginData.buttonInstance) {
                this.registerOnEvent(eventName, function () {
                  pluginData.buttonInstance.toggle(!pluginData.buttonInstance.pressed); // Toggle
                  pluginData.functionality.call(pluginData, pluginData.buttonInstance, pluginData.buttonInstance.pressed); // Call function
                });
              } else {
                this.registerOnEvent(eventName, pluginData.functionality);
              }
            }.bind(this)
          );
        }
      }.bind(this)
    );
  },

  isEqual: function (a, b) {
    return (
      a === b ||
      (a.length === b.length &&
        a.all(function (r) {
          return b.include(r);
        }))
    );
  },

  isDirty: function (a) {
    return a.any(function (shape) {
      return shape.isPropertyChanged();
    });
  },

  setSelection: function (elements, subSelectionElement, force) {
    if (!elements) {
      elements = [];
    }
    if (!(elements instanceof Array)) {
      elements = [elements];
    }

    elements = elements.findAll(function (n) {
      return n && n instanceof ORYX.Core.Shape;
    });

    if (elements[0] instanceof ORYX.Core.Canvas) {
      elements = [];
    }

    if (!force && this.isEqual(this.selection, elements) && !this.isDirty(elements)) {
      return;
    }

    this.selection = elements;
    this._subSelection = subSelectionElement;

    this.handleEvents({ type: ORYX.CONFIG.EVENT_SELECTION_CHANGED, elements: elements, subSelection: subSelectionElement, force: !!force });
  },

  updateSelection: function () {
    this.setSelection(this.selection, this._subSelection, true);
    /*var s = this.selection;
		this.setSelection();
		this.setSelection(s);*/
  },

  getCanvas: function () {
    return this._canvas;
  },

  /**
   *	option = {
   *		type: string,
   *		position: {x:int, y:int},
   *		connectingType:	uiObj-Class
   *		connectedShape: uiObj
   *		draggin: bool
   *		namespace: url
   *       parent: ORYX.Core.AbstractShape
   *		template: a template shape that the newly created inherits properties from.
   *		}
   */
  createShape: function (option) {
    if (option && option.serialize && option.serialize instanceof Array) {
      var type = option.serialize.find(function (obj) {
        return obj.prefix + '-' + obj.name == 'oryx-type';
      });
      var stencil = ORYX.Core.StencilSet.stencil(type.value);

      if (stencil.type() == 'node') {
        var newShapeObject = new ORYX.Core.Node({ eventHandlerCallback: this.handleEvents.bind(this) }, stencil, this._getPluginFacade());
      } else {
        var newShapeObject = new ORYX.Core.Edge({ eventHandlerCallback: this.handleEvents.bind(this) }, stencil, this._getPluginFacade());
      }

      this.getCanvas().add(newShapeObject);
      newShapeObject.deserialize(option.serialize);

      return newShapeObject;
    }

    // If there is no argument, throw an exception
    if (!option || !option.type || !option.namespace) {
      throw 'To create a new shape you have to give an argument with type and namespace';
    }

    var canvas = this.getCanvas();
    var newShapeObject;

    // Get the shape type
    var shapetype = option.type;

    // Get the stencil set
    var sset = ORYX.Core.StencilSet.stencilSet(option.namespace);
    // Create an New Shape, dependents on an Edge or a Node
    if (sset.stencil(shapetype).type() == 'node') {
      newShapeObject = new ORYX.Core.Node(
        { eventHandlerCallback: this.handleEvents.bind(this) },
        sset.stencil(shapetype),
        this._getPluginFacade()
      );
    } else {
      newShapeObject = new ORYX.Core.Edge(
        { eventHandlerCallback: this.handleEvents.bind(this) },
        sset.stencil(shapetype),
        this._getPluginFacade()
      );
    }

    // when there is a template, inherit the properties.
    if (option.template) {
      newShapeObject._jsonStencil.properties = option.template._jsonStencil.properties;
      newShapeObject.postProcessProperties();
    }

    // Add to the canvas
    if (option.parent && newShapeObject instanceof ORYX.Core.Node) {
      option.parent.add(newShapeObject);
    } else {
      canvas.add(newShapeObject);
    }

    // Set the position
    var point = option.position ? option.position : { x: 100, y: 200 };

    var con;
    // If there is create a shape and in the argument there is given an ConnectingType and is instance of an edge
    if (option.connectingType && option.connectedShape && !(newShapeObject instanceof ORYX.Core.Edge)) {
      // there will be create a new Edge
      con = new ORYX.Core.Edge({ eventHandlerCallback: this.handleEvents.bind(this) }, sset.stencil(option.connectingType));

      // And both endings dockers will be referenced to the both shapes
      con.dockers.first().setDockedShape(option.connectedShape);

      var magnet = option.connectedShape.getDefaultMagnet();
      var cPoint = magnet ? magnet.bounds.center() : option.connectedShape.bounds.midPoint();
      con.dockers.first().setReferencePoint(cPoint);
      con.dockers.last().setDockedShape(newShapeObject);
      con.dockers.last().setReferencePoint(newShapeObject.getDefaultMagnet().bounds.center());

      // The Edge will be added to the canvas and be updated
      canvas.add(con);
      //con.update();
    }

    // Move the new Shape to the position
    if (newShapeObject instanceof ORYX.Core.Edge && option.connectedShape) {
      newShapeObject.dockers.first().setDockedShape(option.connectedShape);

      if (option.connectedShape instanceof ORYX.Core.Node) {
        newShapeObject.dockers.first().setReferencePoint(option.connectedShape.getDefaultMagnet().bounds.center());
        newShapeObject.dockers.last().bounds.centerMoveTo(point);
      } else {
        newShapeObject.dockers.first().setReferencePoint(option.connectedShape.bounds.midPoint());
      }

      var start = newShapeObject.dockers.first();
      var end = newShapeObject.dockers.last();

      if (start.getDockedShape() && end.getDockedShape()) {
        var startPoint = start.getAbsoluteReferencePoint();
        var endPoint = end.getAbsoluteReferencePoint();

        var docker = newShapeObject.createDocker();
        docker.bounds.centerMoveTo({
          x: startPoint.x + (endPont.x - startPoint.x) / 2,
          y: startPoint.y + (endPont.y - startPoint.y) / 2,
        });
      }
    } else {
      var b = newShapeObject.bounds;
      if (newShapeObject instanceof ORYX.Core.Node && newShapeObject.dockers.length == 1) {
        b = newShapeObject.dockers.first().bounds;
      }

      b.centerMoveTo(point);

      var upL = b.upperLeft();
      b.moveBy(-Math.min(upL.x, 0), -Math.min(upL.y, 0));

      var lwR = b.lowerRight();
      b.moveBy(-Math.max(lwR.x - canvas.bounds.width(), 0), -Math.max(lwR.y - canvas.bounds.height(), 0));
    }

    // Update the shape
    if (newShapeObject instanceof ORYX.Core.Edge) {
      newShapeObject._update(false);
    }

    // And refresh the selection
    if (!(newShapeObject instanceof ORYX.Core.Edge) && !option.dontUpdateSelection) {
      this.setSelection([newShapeObject]);
    }

    if (con && con.alignDockers) {
      //con.alignDockers();
    }
    if (newShapeObject.alignDockers) {
      newShapeObject.alignDockers();
    }

    return newShapeObject;
  },

  deleteShape: function (shape) {
    if (!shape || !shape.parent) {
      return;
    }

    //remove shape from parent
    // this also removes it from DOM
    shape.parent.remove(shape);

    //delete references to outgoing edges
    shape.getOutgoingShapes().each(function (os) {
      var docker = os.getDockers().first();
      if (docker && docker.getDockedShape() == shape) {
        docker.setDockedShape(undefined);
      }
    });

    //delete references to incoming edges
    shape.getIncomingShapes().each(function (is) {
      var docker = is.getDockers().last();
      if (docker && docker.getDockedShape() == shape) {
        docker.setDockedShape(undefined);
      }
    });

    //delete references of the shape's dockers
    shape.getDockers().each(function (docker) {
      docker.setDockedShape(undefined);
    });
  },

  /**
   * Returns an object with meta data about the model.
   * Like name, description, ...
   *
   * Empty object with the current backend.
   *
   * @return {Object} Meta data about the model
   */
  getModelMetaData: function () {
    return this.modelMetaData;
  },

  /* Event-Handler Methods */

  /**
   * Helper method to execute an event immediately. The event is not
   * scheduled in the _eventsQueue. Needed to handle Layout-Callbacks.
   */
  _executeEventImmediately: function (eventObj) {
    if (this.DOMEventListeners.keys().member(eventObj.event.type)) {
      this.DOMEventListeners[eventObj.event.type].each(
        function (value) {
          value(eventObj.event, eventObj.arg);
        }.bind(this)
      );
    }
  },

  _executeEvents: function () {
    this._queueRunning = true;
    while (this._eventsQueue.length > 0) {
      var val = this._eventsQueue.shift();
      this._executeEventImmediately(val);
    }
    this._queueRunning = false;
  },

  /**
   * Leitet die Events an die Editor-Spezifischen Event-Methoden weiter
   * @param {Object} event Event , welches gefeuert wurde
   * @param {Object} uiObj Target-UiObj
   */
  handleEvents: function (event, uiObj) {
    ORYX.Log.trace('Dispatching event type %0 on %1', event.type, uiObj);

    switch (event.type) {
      case ORYX.CONFIG.EVENT_MOUSEDOWN:
        this._handleMouseDown(event, uiObj);
        break;
      case ORYX.CONFIG.EVENT_MOUSEMOVE:
        this._handleMouseMove(event, uiObj);
        break;
      case ORYX.CONFIG.EVENT_MOUSEUP:
        this._handleMouseUp(event, uiObj);
        break;
      case ORYX.CONFIG.EVENT_MOUSEOVER:
        this._handleMouseHover(event, uiObj);
        break;
      case ORYX.CONFIG.EVENT_MOUSEOUT:
        this._handleMouseOut(event, uiObj);
        break;
    }
    /* Force execution if necessary. Used while handle Layout-Callbacks. */
    if (event.forceExecution) {
      this._executeEventImmediately({ event: event, arg: uiObj });
    } else {
      this._eventsQueue.push({ event: event, arg: uiObj });
    }

    if (!this._queueRunning) {
      this._executeEvents();
    }

    // TODO: Make this return whether no listener returned false.
    // So that, when one considers bubbling undesireable, it won't happen.
    return false;
  },

  isValidEvent: function (e) {
    try {
      var isInput = ['INPUT', 'TEXTAREA'].include(e.target.tagName.toUpperCase());
      var gridHasFocus = e.target.className.include('x-grid3-focus') && !e.target.className.include('x-grid3-focus-canvas');
      return !isInput && !gridHasFocus;
    } catch (e) {
      return false;
    }
  },

  catchKeyUpEvents: function (event) {
    if (!this._keyupEnabled) {
      return;
    }
    /* assure we have the current event. */
    if (!event) event = window.event;

    // Checks if the event comes from some input field
    if (!this.isValidEvent(event)) {
      return;
    }

    /* Create key up event type */
    var keyUpEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_UP);

    ORYX.Log.debug('Key Event to handle: %0', keyUpEvent);

    /* forward to dispatching. */
    this.handleEvents({ type: keyUpEvent, event: event });
  },

  /**
   * Catches all key down events and forward the appropriated event to
   * dispatching concerning to the pressed keys.
   *
   * @param {Event}
   * 		The key down event to handle
   */
  catchKeyDownEvents: function (event) {
    if (!this._keydownEnabled) {
      return;
    }
    /* Assure we have the current event. */
    if (!event) event = window.event;

    /* Fixed in FF3 */
    // This is a mac-specific fix. The mozilla event object has no knowledge
    // of meta key modifier on osx, however, it is needed for certain
    // shortcuts. This fix adds the metaKey field to the event object, so
    // that all listeners that registered per Oryx plugin facade profit from
    // this. The original bug is filed in
    // https://bugzilla.mozilla.org/show_bug.cgi?id=418334
    //if (this.__currentKey == ORYX.CONFIG.KEY_CODE_META) {
    //	event.appleMetaKey = true;
    //}
    //this.__currentKey = pressedKey;

    // Checks if the event comes from some input field
    if (!this.isValidEvent(event)) {
      return;
    }

    /* Create key up event type */
    var keyDownEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_DOWN);

    ORYX.Log.debug('Key Event to handle: %0', keyDownEvent);

    /* Forward to dispatching. */
    this.handleEvents({ type: keyDownEvent, event: event });
  },

  /**
   * Creates the event type name concerning to the pressed keys.
   *
   * @param {Event} keyDownEvent
   * 		The source keyDownEvent to build up the event name
   */
  createKeyCombEvent: function (keyEvent, keyAction) {
    /* Get the currently pressed key code. */
    var pressedKey = keyEvent.which || keyEvent.keyCode;
    //this.__currentKey = pressedKey;

    /* Event name */
    var eventName = 'key.event';

    /* Key action */
    if (keyAction) {
      eventName += '.' + keyAction;
    }

    /* Ctrl or apple meta key is pressed */
    if (keyEvent.ctrlKey || keyEvent.metaKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_META_CTRL;
    }

    /* Alt key is pressed */
    if (keyEvent.altKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_ALT;
    }

    /* Alt key is pressed */
    if (keyEvent.shiftKey) {
      eventName += '.' + ORYX.CONFIG.META_KEY_SHIFT;
    }

    /* Return the composed event name */
    return eventName + '.' + pressedKey;
  },

  _handleMouseDown: function (event, uiObj) {
    // get canvas.
    var canvas = this.getCanvas();
    // Try to get the focus
    canvas.focus();

    // find the shape that is responsible for this element's id.
    var element = event.currentTarget;
    var elementController = uiObj;

    // gather information on selection.
    var currentIsSelectable = elementController !== null && elementController !== undefined && elementController.isSelectable;
    var currentIsMovable = elementController !== null && elementController !== undefined && elementController.isMovable;
    var modifierKeyPressed = event.shiftKey || event.ctrlKey;
    var noObjectsSelected = this.selection.length === 0;
    var currentIsSelected = this.selection.member(elementController);

    // Rule #1: When there is nothing selected, select the clicked object.
    if (currentIsSelectable && noObjectsSelected) {
      this.setSelection([elementController]);

      ORYX.Log.trace('Rule #1 applied for mouse down on %0', element.id);

      // Rule #3: When at least one element is selected, and there is no
      // control key pressed, and the clicked object is not selected, select
      // the clicked object.
    } else if (currentIsSelectable && !noObjectsSelected && !modifierKeyPressed && !currentIsSelected) {
      this.setSelection([elementController]);

      //var objectType = elementController.readAttributes();
      //alert(objectType[0] + ": " + objectType[1]);

      ORYX.Log.trace('Rule #3 applied for mouse down on %0', element.id);

      // Rule #4: When the control key is pressed, and the current object is
      // not selected, add it to the selection.
    } else if (currentIsSelectable && modifierKeyPressed && !currentIsSelected) {
      var newSelection = this.selection.clone();
      newSelection.push(elementController);
      this.setSelection(newSelection);

      ORYX.Log.trace('Rule #4 applied for mouse down on %0', element.id);

      // Rule #6
    } else if (currentIsSelectable && currentIsSelected && modifierKeyPressed) {
      var newSelection = this.selection.clone();
      this.setSelection(newSelection.without(elementController));

      ORYX.Log.trace('Rule #6 applied for mouse down on %0', elementController.id);

      // Rule #5: When there is at least one object selected and no control
      // key pressed, we're dragging.
      /*} else if(currentIsSelectable && !noObjectsSelected
			&& !modifierKeyPressed) {

			if(this.log.isTraceEnabled())
				this.log.trace("Rule #5 applied for mouse down on "+element.id);
*/
      // Rule #2: When clicked on something that is neither
      // selectable nor movable, clear the selection, and return.
    } else if (!currentIsSelectable && !currentIsMovable) {
      this.setSelection([]);

      ORYX.Log.trace('Rule #2 applied for mouse down on %0', element.id);

      return;

      // Rule #7: When the current object is not selectable but movable,
      // it is probably a control. Leave the selection unchanged but set
      // the movedObject to the current one and enable Drag. Dockers will
      // be processed in the dragDocker plugin.
    } else if (!currentIsSelectable && currentIsMovable && !(elementController instanceof ORYX.Core.Controls.Docker)) {
      // TODO: If there is any moveable elements, do this in a plugin
      //ORYX.Core.UIEnableDrag(event, elementController);

      ORYX.Log.trace('Rule #7 applied for mouse down on %0', element.id);

      // Rule #8: When the element is selectable and is currently selected and no
      // modifier key is pressed
    } else if (currentIsSelectable && currentIsSelected && !modifierKeyPressed) {
      this._subSelection = this._subSelection != elementController ? elementController : undefined;

      this.setSelection(this.selection, this._subSelection);

      ORYX.Log.trace('Rule #8 applied for mouse down on %0', element.id);
    }

    // prevent event from bubbling, return.
    //Event.stop(event);
    return;
  },

  _handleMouseMove: function (event, uiObj) {
    return;
  },

  _handleMouseUp: function (event, uiObj) {
    // get canvas.
    var canvas = this.getCanvas();

    // find the shape that is responsible for this elemement's id.
    var elementController = uiObj;

    //get event position
    var evPos = this.eventCoordinates(event);

    //Event.stop(event);
  },

  _handleMouseHover: function (event, uiObj) {
    return;
  },

  _handleMouseOut: function (event, uiObj) {
    return;
  },

  /**
   * Calculates the event coordinates to SVG document coordinates.
   * @param {Event} event
   * @return {SVGPoint} The event coordinates in the SVG document
   */
  eventCoordinates: function (event) {
    var canvas = this.getCanvas();

    var svgPoint = canvas.node.ownerSVGElement.createSVGPoint();
    svgPoint.x = event.clientX;
    svgPoint.y = event.clientY;

    var additionalIEZoom = 1;
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent;
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100;
        }
      }
    }

    if (additionalIEZoom !== 1) {
      svgPoint.x = svgPoint.x * additionalIEZoom;
      svgPoint.y = svgPoint.y * additionalIEZoom;
    }

    var matrix = canvas.node.getScreenCTM();
    return svgPoint.matrixTransform(matrix.inverse());
  },

  eventCoordinatesXY: function (x, y) {
    var canvas = this.getCanvas();

    var svgPoint = canvas.node.ownerSVGElement.createSVGPoint();
    svgPoint.x = x;
    svgPoint.y = y;

    var additionalIEZoom = 1;
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent;
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100;
        }
      }
    }

    if (additionalIEZoom !== 1) {
      svgPoint.x = svgPoint.x * additionalIEZoom;
      svgPoint.y = svgPoint.y * additionalIEZoom;
    }

    var matrix = canvas.node.getScreenCTM();
    return svgPoint.matrixTransform(matrix.inverse());
  },
};
ORYX.Editor = Clazz.extend(ORYX.Editor);

/**
 * Creates a new ORYX.Editor instance by fetching a model from given url and passing it to the constructur
 * @param {String} modelUrl The JSON URL of a model.
 * @param {Object} config Editor config passed to the constructur, merged with the response of the request to modelUrl
 */
ORYX.Editor.createByUrl = function (modelUrl) {
  new Ajax.Request(modelUrl, {
    method: 'GET',
    onSuccess: function (transport) {
      var editorConfig = JSON.parse(transport.responseText);
      new ORYX.Editor(editorConfig);
    }.bind(this),
  });
};

// TODO Implement namespace awareness on attribute level.
/**
 * graft() function
 * Originally by Sean M. Burke from interglacial.com, altered for usage with
 * SVG and namespace (xmlns) support. Be sure you understand xmlns before
 * using this funtion, as it creates all grafted elements in the xmlns
 * provided by you and all element's attribures in default xmlns. If you
 * need to graft elements in a certain xmlns and wish to assign attributes
 * in both that and another xmlns, you will need to do stepwise grafting,
 * adding non-default attributes yourself or you'll have to enhance this
 * function. Latter, I would appreciate: martin???apfelfabrik.de
 * @param {Object} namespace The namespace in which
 * 					elements should be grafted.
 * @param {Object} parent The element that should contain the grafted
 * 					structure after the function returned.
 * @param {Object} t the crafting structure.
 * @param {Object} doc the document in which grafting is performed.
 */
ORYX.Editor.graft = function (namespace, parent, t, doc) {
  doc = doc || (parent && parent.ownerDocument) || document;
  var e;
  if (t === undefined) {
    throw "Can't graft an undefined value";
  } else if (t.constructor == String) {
    e = doc.createTextNode(t);
  } else {
    for (var i = 0; i < t.length; i++) {
      if (i === 0 && t[i].constructor == String) {
        var snared;
        snared = t[i].match(/^([a-z][a-z0-9]*)\.([^\s\.]+)$/i);
        if (snared) {
          e = doc.createElementNS(namespace, snared[1]);
          e.setAttributeNS(null, 'class', snared[2]);
          continue;
        }
        snared = t[i].match(/^([a-z][a-z0-9]*)$/i);
        if (snared) {
          e = doc.createElementNS(namespace, snared[1]); // but no class
          continue;
        }

        // Otherwise:
        e = doc.createElementNS(namespace, 'span');
        e.setAttribute(null, 'class', 'namelessFromLOL');
      }

      if (t[i] === undefined) {
        throw "Can't graft an undefined value in a list!";
      } else if (t[i].constructor == String || t[i].constructor == Array) {
        this.graft(namespace, e, t[i], doc);
      } else if (t[i].constructor == Number) {
        this.graft(namespace, e, t[i].toString(), doc);
      } else if (t[i].constructor == Object) {
        // hash's properties => element's attributes
        for (var k in t[i]) {
          e.setAttributeNS(null, k, t[i][k]);
        }
      } else {
      }
    }
  }
  if (parent && parent.appendChild) {
    parent.appendChild(e);
  } else {
  }
  return e; // return the topmost created node
};

ORYX.Editor.provideId = function () {
  var res = [],
    hex = '0123456789ABCDEF';

  for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random() * 0x10);

  res[14] = 4;
  res[19] = (res[19] & 0x3) | 0x8;

  for (var i = 0; i < 36; i++) res[i] = hex[res[i]];

  res[8] = res[13] = res[18] = res[23] = '-';

  return 'oryx_' + res.join('');
};

/**
 * When working with Ext, conditionally the window needs to be resized. To do
 * so, use this class method. Resize is deferred until 100ms, and all subsequent
 * resizeBugFix calls are ignored until the initially requested resize is
 * performed.
 */
ORYX.Editor.resizeFix = function () {
  if (!ORYX.Editor._resizeFixTimeout) {
    ORYX.Editor._resizeFixTimeout = window.setTimeout(function () {
      window.resizeBy(1, 1);
      window.resizeBy(-1, -1);
      ORYX.Editor._resizefixTimeout = null;
    }, 100);
  }
};

ORYX.Editor.Cookie = {
  callbacks: [],

  onChange: function (callback, interval) {
    this.callbacks.push(callback);
    this.start(interval);
  },

  start: function (interval) {
    if (this.pe) {
      return;
    }

    var currentString = document.cookie;

    this.pe = new PeriodicalExecuter(
      function () {
        if (currentString != document.cookie) {
          currentString = document.cookie;
          this.callbacks.each(
            function (callback) {
              callback(this.getParams());
            }.bind(this)
          );
        }
      }.bind(this),
      (interval || 10000) / 1000
    );
  },

  stop: function () {
    if (this.pe) {
      this.pe.stop();
      this.pe = null;
    }
  },

  getParams: function () {
    var res = {};

    var p = document.cookie;
    p.split('; ').each(function (param) {
      res[param.split('=')[0]] = param.split('=')[1];
    });

    return res;
  },

  toString: function () {
    return document.cookie;
  },
};

/**
 * Workaround for SAFARI/Webkit, because
 * when trying to check SVGSVGElement of instanceof there is
 * raising an error
 *
 */
ORYX.Editor.SVGClassElementsAreAvailable = true;
ORYX.Editor.setMissingClasses = function () {
  try {
    SVGElement;
  } catch (e) {
    ORYX.Editor.SVGClassElementsAreAvailable = false;
    SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg').toString();
    SVGGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g').toString();
    SVGPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path').toString();
    SVGTextElement = document.createElementNS('http://www.w3.org/2000/svg', 'text').toString();
    //SVGMarkerElement 	= document.createElementNS('http://www.w3.org/2000/svg', 'marker').toString();
    SVGRectElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect').toString();
    SVGImageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image').toString();
    SVGCircleElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle').toString();
    SVGEllipseElement = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse').toString();
    SVGLineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line').toString();
    SVGPolylineElement = document.createElementNS('http://www.w3.org/2000/svg', 'polyline').toString();
    SVGPolygonElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon').toString();
  }
};
ORYX.Editor.checkClassType = function (classInst, classType) {
  if (ORYX.Editor.SVGClassElementsAreAvailable) {
    return classInst instanceof classType;
  } else {
    return classInst == classType;
  }
};
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

new (function () {
  ORYX.Core.UIEnableDrag = function (event, uiObj, option) {
    this.uiObj = uiObj;
    var upL = uiObj.bounds.upperLeft();

    var a = uiObj.node.getScreenCTM();
    this.faktorXY = { x: a.a, y: a.d };

    this.scrollNode = uiObj.node.ownerSVGElement.parentNode.parentNode;

    this.offSetPosition = {
      x: Event.pointerX(event) - upL.x * this.faktorXY.x,
      y: Event.pointerY(event) - upL.y * this.faktorXY.y,
    };

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop };

    this.dragCallback = ORYX.Core.UIDragCallback.bind(this);
    this.disableCallback = ORYX.Core.UIDisableDrag.bind(this);

    this.movedCallback = option ? option.movedCallback : undefined;
    this.upCallback = option ? option.upCallback : undefined;

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true);
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback, false);
  };

  ORYX.Core.UIDragCallback = function (event) {
    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y,
    };

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft;
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop;

    position.x /= this.faktorXY.x;
    position.y /= this.faktorXY.y;

    this.uiObj.bounds.moveTo(position);
    //this.uiObj.update();

    if (this.movedCallback) this.movedCallback(event);

    //Event.stop(event);
  };

  ORYX.Core.UIDisableDrag = function (event) {
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback, false);
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true);

    if (this.upCallback) this.upCallback(event);

    this.upCallback = undefined;
    this.movedCallback = undefined;

    Event.stop(event);
  };

  /**
   * Implements a command to move docker by an offset.
   *
   * @class ORYX.Core.MoveDockersCommand
   * @param {Object} object An object with the docker id as key and docker and offset as object value
   *
   */
  ORYX.Core.MoveDockersCommand = ORYX.Core.Command.extend({
    construct: function (dockers) {
      this.dockers = $H(dockers);
      this.edges = $H({});
    },
    execute: function () {
      if (this.changes) {
        this.executeAgain();
        return;
      } else {
        this.changes = $H({});
      }

      this.dockers.values().each(
        function (docker) {
          var edge = docker.docker.parent;
          if (!edge) {
            return;
          }

          if (!this.changes[edge.getId()]) {
            this.changes[edge.getId()] = {
              edge: edge,
              oldDockerPositions: edge.dockers.map(function (r) {
                return r.bounds.center();
              }),
            };
          }
          docker.docker.bounds.moveBy(docker.offset);
          this.edges[edge.getId()] = edge;
          docker.docker.update();
        }.bind(this)
      );
      this.edges.each(
        function (edge) {
          this.updateEdge(edge.value);
          if (this.changes[edge.value.getId()])
            this.changes[edge.value.getId()].dockerPositions = edge.value.dockers.map(function (r) {
              return r.bounds.center();
            });
        }.bind(this)
      );
    },
    updateEdge: function (edge) {
      edge._update(true);
      [edge.getOutgoingShapes(), edge.getIncomingShapes()].flatten().invoke('_update', [true]);
    },
    executeAgain: function () {
      this.changes.values().each(
        function (change) {
          // Reset the dockers
          this.removeAllDocker(change.edge);
          change.dockerPositions.each(
            function (pos, i) {
              if (i == 0 || i == change.dockerPositions.length - 1) {
                return;
              }
              var docker = change.edge.createDocker(undefined, pos);
              docker.bounds.centerMoveTo(pos);
              docker.update();
            }.bind(this)
          );
          this.updateEdge(change.edge);
        }.bind(this)
      );
    },
    rollback: function () {
      this.changes.values().each(
        function (change) {
          // Reset the dockers
          this.removeAllDocker(change.edge);
          change.oldDockerPositions.each(
            function (pos, i) {
              if (i == 0 || i == change.oldDockerPositions.length - 1) {
                return;
              }
              var docker = change.edge.createDocker(undefined, pos);
              docker.bounds.centerMoveTo(pos);
              docker.update();
            }.bind(this)
          );
          this.updateEdge(change.edge);
        }.bind(this)
      );
    },
    removeAllDocker: function (edge) {
      edge.dockers.slice(1, edge.dockers.length - 1).each(function (docker) {
        edge.removeDocker(docker);
      });
    },
  });
})();
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription Base class for Shapes.
 * @extends ORYX.Core.AbstractShape
 */
ORYX.Core.Shape = {
  /**
   * Constructor
   */
  construct: function (options, stencil, facade) {
    // call base class constructor
    arguments.callee.$.construct.apply(this, arguments);

    this.facade = facade;
    this.dockers = [];
    this.magnets = [];

    this._defaultMagnet;

    this.incoming = [];
    this.outgoing = [];

    this.nodes = [];

    this._dockerChangedCallback = this._dockerChanged.bind(this);

    //Hash map for all labels. Labels are not treated as children of shapes.
    this._labels = new Hash();

    // create SVG node
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'g',
      { id: 'svg-' + this.resourceId },
      [
        'g',
        { class: 'stencils' },
        ['g', { class: 'me' }],
        ['g', { class: 'children', style: 'overflow:hidden' }],
        ['g', { class: 'edge' }],
      ],
      ['g', { class: 'controls' }, ['g', { class: 'dockers' }], ['g', { class: 'magnets' }]],
    ]);
  },

  /**
   * If changed flag is set, refresh method is called.
   */
  update: function () {
    //if(this.isChanged) {
    //this.layout();
    //}
  },

  /**
   * !!!Not called from any sub class!!!
   */
  _update: function () {},

  /**
   * Calls the super class refresh method
   *  and updates the svg elements that are referenced by a property.
   */
  refresh: function () {
    //call base class refresh method
    arguments.callee.$.refresh.apply(this, arguments);

    if (this.node.ownerDocument) {
      //adjust SVG to properties' values
      var me = this;
      this.propertiesChanged.each(
        function (propChanged) {
          if (propChanged.value) {
            var prop = this.properties[propChanged.key];
            var property = this.getStencil().property(propChanged.key);
            if (property != undefined) {
              this.propertiesChanged[propChanged.key] = false;

              //handle choice properties
              if (property.type() == ORYX.CONFIG.TYPE_CHOICE) {
                //iterate all references to SVG elements
                property.refToView().each(
                  function (ref) {
                    //if property is referencing a label, update the label
                    if (ref !== '') {
                      var label = this._labels[this.id + ref];
                      if (label && property.item(prop)) {
                        label.text(property.item(prop).title());
                      }
                    }
                  }.bind(this)
                );

                //if the choice's items are referencing SVG elements
                // show the selected and hide all other referenced SVG
                // elements
                var refreshedSvgElements = new Hash();
                property.items().each(
                  function (item) {
                    item.refToView().each(
                      function (itemRef) {
                        if (itemRef == '') {
                          return;
                        }

                        var svgElem = this.node.ownerDocument.getElementById(this.id + itemRef);

                        if (!svgElem) {
                          return;
                        }

                        /* Do not refresh the same svg element multiple times */
                        if (!refreshedSvgElements[svgElem.id] || prop == item.value()) {
                          svgElem.setAttributeNS(null, 'display', prop == item.value() ? 'inherit' : 'none');
                          refreshedSvgElements[svgElem.id] = svgElem;
                        }

                        // Reload the href if there is an image-tag
                        if (ORYX.Editor.checkClassType(svgElem, SVGImageElement)) {
                          svgElem.setAttributeNS(
                            'http://www.w3.org/1999/xlink',
                            'href',
                            svgElem.getAttributeNS('http://www.w3.org/1999/xlink', 'href')
                          );
                        }
                      }.bind(this)
                    );
                  }.bind(this)
                );
              } else {
                //handle properties that are not of type choice
                //iterate all references to SVG elements
                property.refToView().each(
                  function (ref) {
                    //if the property does not reference an SVG element,
                    // do nothing

                    if (ref === '') {
                      return;
                    }

                    var refId = this.id + ref;

                    if (property.type() === ORYX.CONFIG.TYPE_KISBPM_MULTIINSTANCE) {
                      if (ref === 'multiinstance') {
                        var svgElemParallel = this.node.ownerDocument.getElementById(this.id + 'parallel');
                        if (svgElemParallel) {
                          if (prop === 'Parallel') {
                            svgElemParallel.setAttributeNS(null, 'display', 'inherit');
                          } else {
                            svgElemParallel.setAttributeNS(null, 'display', 'none');
                          }
                        }

                        var svgElemSequential = this.node.ownerDocument.getElementById(this.id + 'sequential');

                        if (svgElemSequential) {
                          if (prop === 'Sequential') {
                            svgElemSequential.setAttributeNS(null, 'display', 'inherit');
                          } else {
                            svgElemSequential.setAttributeNS(null, 'display', 'none');
                          }
                        }
                      }
                      return;
                    } else if (property.type() === 'cancelactivity') {
                      var svgElemFrame = this.node.ownerDocument.getElementById(this.id + 'frame');
                      var svgElemFrame2 = this.node.ownerDocument.getElementById(this.id + 'frame2');

                      if (prop === 'true') {
                        svgElemFrame.setAttributeNS(null, 'display', 'inherit');
                        svgElemFrame2.setAttributeNS(null, 'display', 'inherit');
                      } else {
                        svgElemFrame.setAttributeNS(null, 'display', 'none');
                        svgElemFrame2.setAttributeNS(null, 'display', 'none');
                      }
                    }

                    //get the SVG element
                    var svgElem = this.node.ownerDocument.getElementById(refId);

                    //if the SVG element can not be found
                    if (!svgElem || !svgElem.ownerSVGElement) {
                      //if the referenced SVG element is a SVGAElement, it cannot
                      // be found with getElementById (Firefox bug).
                      // this is a work around
                      if (property.type() === ORYX.CONFIG.TYPE_URL || property.type() === ORYX.CONFIG.TYPE_DIAGRAM_LINK) {
                        var svgElems = this.node.ownerDocument.getElementsByTagNameNS('http://www.w3.org/2000/svg', 'a');

                        svgElem = $A(svgElems).find(function (elem) {
                          return elem.getAttributeNS(null, 'id') === refId;
                        });

                        if (!svgElem) {
                          return;
                        }
                      } else {
                        //this.propertiesChanged[propChanged.key] = true;
                        return;
                      }
                    }

                    if (property.complexAttributeToView()) {
                      var label = this._labels[refId];
                      if (label) {
                        try {
                          propJson = prop.evalJSON();
                          var value = propJson[property.complexAttributeToView()];
                          label.text(value ? value : prop);
                        } catch (e) {
                          label.text(prop);
                        }
                      }
                    } else {
                      switch (property.type()) {
                        case ORYX.CONFIG.TYPE_BOOLEAN:
                          if (typeof prop == 'string') prop = prop === 'true';

                          svgElem.setAttributeNS(null, 'display', !(prop === property.inverseBoolean()) ? 'inherit' : 'none');

                          break;
                        case ORYX.CONFIG.TYPE_COLOR:
                          if (property.fill()) {
                            if (svgElem.tagName.toLowerCase() === 'stop') {
                              if (prop) {
                                if (property.lightness() && property.lightness() !== 1) {
                                  prop = ORYX.Utils.adjustLightness(prop, property.lightness());
                                }

                                svgElem.setAttributeNS(null, 'stop-color', prop);

                                // Adjust stop color of the others
                                if (svgElem.parentNode.tagName.toLowerCase() === 'radialgradient') {
                                  ORYX.Utils.adjustGradient(svgElem.parentNode, svgElem);
                                }
                              }

                              // If there is no value, set opaque
                              if (svgElem.parentNode.tagName.toLowerCase() === 'radialgradient') {
                                $A(svgElem.parentNode.getElementsByTagName('stop')).each(
                                  function (stop) {
                                    stop.setAttributeNS(
                                      null,
                                      'stop-opacity',
                                      prop ? stop.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'default-stop-opacity') || 1 : 0
                                    );
                                  }.bind(this)
                                );
                              }
                            } else {
                              svgElem.setAttributeNS(null, 'fill', prop);
                            }
                          }
                          if (property.stroke()) {
                            svgElem.setAttributeNS(null, 'stroke', prop);
                          }
                          break;
                        case ORYX.CONFIG.TYPE_STRING:
                          var label = this._labels[refId];
                          if (label) {
                            label.text(prop);
                          }
                          break;
                        case ORYX.CONFIG.TYPE_EXPRESSION:
                          var label = this._labels[refId];
                          if (label) {
                            label.text(prop);
                          }
                          break;
                        case ORYX.CONFIG.TYPE_DATASOURCE:
                          var label = this._labels[refId];
                          if (label) {
                            label.text(prop);
                          }
                          break;
                        case ORYX.CONFIG.TYPE_INTEGER:
                          var label = this._labels[refId];
                          if (label) {
                            label.text(prop);
                          }
                          break;
                        case ORYX.CONFIG.TYPE_FLOAT:
                          if (property.fillOpacity()) {
                            svgElem.setAttributeNS(null, 'fill-opacity', prop);
                          }
                          if (property.strokeOpacity()) {
                            svgElem.setAttributeNS(null, 'stroke-opacity', prop);
                          }
                          if (!property.fillOpacity() && !property.strokeOpacity()) {
                            var label = this._labels[refId];
                            if (label) {
                              label.text(prop);
                            }
                          }
                          break;

                        case ORYX.CONFIG.TYPE_FORM_LINK:
                          if (ref == 'pimg') {
                            var onclickAttr = svgElem.getAttributeNodeNS('', 'onclick');
                            if (onclickAttr) {
                              if (prop && ('' + prop).length > 0) {
                                onclickAttr.textContent = "window.location = '../service/editor?id=" + prop + "_form'";
                              } else {
                                newFormFacade = this.facade;
                                onclickAttr.textContent = "displayNewFormDialog('" + this.resourceId + "');";
                              }
                            }
                          } else if (ref == 'linkIndicator') {
                            if (prop && prop.length > 0) {
                              svgElem.setAttributeNS(null, 'display', 'inherit');
                            } else {
                              svgElem.setAttributeNS(null, 'display', 'none');
                            }
                          }
                          break;
                        case ORYX.CONFIG.TYPE_URL:
                        case ORYX.CONFIG.TYPE_DIAGRAM_LINK:
                          //TODO what is the dafault path?
                          var hrefAttr = svgElem.getAttributeNodeNS('http://www.w3.org/1999/xlink', 'xlink:href');
                          if (hrefAttr) {
                            hrefAttr.textContent = prop;
                          } else {
                            svgElem.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', prop);
                          }
                          break;
                      }
                    }
                  }.bind(this)
                );
              }
            }
          }
        }.bind(this)
      );

      //update labels
      this._labels.values().each(function (label) {
        label.update();
      });
    }
  },

  layout: function () {
    //this.getStencil().layout(this)
    var layoutEvents = this.getStencil().layout();
    if (layoutEvents) {
      layoutEvents.each(
        function (event) {
          // setup additional attributes
          event.shape = this;
          event.forceExecution = true;

          // do layouting
          this._delegateEvent(event);
        }.bind(this)
      );
    }
  },

  /**
   * Returns an array of Label objects.
   */
  getLabels: function () {
    return this._labels.values();
  },

  /**
   * Returns the label for a given ref
   * @return {ORYX.Core.Label} Returns null if there is no label
   */
  getLabel: function (ref) {
    if (!ref) {
      return null;
    }
    return (
      (
        this._labels.find(function (o) {
          return o.key.endsWith(ref);
        }) || {}
      ).value || null
    );
  },

  /**
   * Hides all related labels
   *
   */
  hideLabels: function () {
    this.getLabels().invoke('hide');
  },

  /**
   * Shows all related labels
   *
   */
  showLabels: function () {
    var labels = this.getLabels();
    labels.invoke('show');
    labels.each(function (label) {
      label.update();
    });
  },

  setOpacity: function (value, animate) {
    value = Math.max(Math.min(typeof value == 'number' ? value : 1.0, 1.0), 0.0);

    if (value !== 1.0) {
      value = String(value);
      this.node.setAttributeNS(null, 'fill-opacity', value);
      this.node.setAttributeNS(null, 'stroke-opacity', value);
    } else {
      this.node.removeAttributeNS(null, 'fill-opacity');
      this.node.removeAttributeNS(null, 'stroke-opacity');
    }
  },

  /**
   * Returns an array of dockers of this object.
   */
  getDockers: function () {
    return this.dockers;
  },

  getMagnets: function () {
    return this.magnets;
  },

  getDefaultMagnet: function () {
    if (this._defaultMagnet) {
      return this._defaultMagnet;
    } else if (this.magnets.length > 0) {
      return this.magnets[0];
    } else {
      return undefined;
    }
  },

  getParentShape: function () {
    return this.parent;
  },

  getIncomingShapes: function (iterator) {
    if (iterator) {
      this.incoming.each(iterator);
    }
    return this.incoming;
  },

  getIncomingNodes: function (iterator) {
    return this.incoming.select(function (incoming) {
      var isNode = incoming instanceof ORYX.Core.Node;
      if (isNode && iterator) iterator(incoming);
      return isNode;
    });
  },

  getOutgoingShapes: function (iterator) {
    if (iterator) {
      this.outgoing.each(iterator);
    }
    return this.outgoing;
  },

  getOutgoingNodes: function (iterator) {
    return this.outgoing.select(function (out) {
      var isNode = out instanceof ORYX.Core.Node;
      if (isNode && iterator) iterator(out);
      return isNode;
    });
  },

  getAllDockedShapes: function (iterator) {
    var result = this.incoming.concat(this.outgoing);
    if (iterator) {
      result.each(iterator);
    }
    return result;
  },

  getCanvas: function () {
    if (this.parent instanceof ORYX.Core.Canvas) {
      return this.parent;
    } else if (this.parent instanceof ORYX.Core.Shape) {
      return this.parent.getCanvas();
    } else {
      return undefined;
    }
  },

  /**
   *
   * @param {Object} deep
   * @param {Object} iterator
   */
  getChildNodes: function (deep, iterator) {
    if (!deep && !iterator) {
      return this.nodes.clone();
    } else {
      var result = [];
      this.nodes.each(function (uiObject) {
        if (!uiObject.isVisible) {
          return;
        }
        if (iterator) {
          iterator(uiObject);
        }
        result.push(uiObject);

        if (deep && uiObject instanceof ORYX.Core.Shape) {
          result = result.concat(uiObject.getChildNodes(deep, iterator));
        }
      });

      return result;
    }
  },

  /**
   * Overrides the UIObject.add method. Adds uiObject to the correct sub node.
   * @param {UIObject} uiObject
   * @param {Number} index
   */
  add: function (uiObject, index, silent) {
    //parameter has to be an UIObject, but
    // must not be an Edge.
    if (uiObject instanceof ORYX.Core.UIObject && !(uiObject instanceof ORYX.Core.Edge)) {
      if (!this.children.member(uiObject)) {
        //if uiObject is child of another parent, remove it from that parent.
        if (uiObject.parent) {
          uiObject.parent.remove(uiObject, true);
        }

        //add uiObject to this Shape
        if (index != undefined) this.children.splice(index, 0, uiObject);
        else this.children.push(uiObject);

        //set parent reference
        uiObject.parent = this;

        //add uiObject.node to this.node depending on the type of uiObject
        var parent;
        if (uiObject instanceof ORYX.Core.Node) {
          parent = this.node.childNodes[0].childNodes[1];
          this.nodes.push(uiObject);
        } else if (uiObject instanceof ORYX.Core.Controls.Control) {
          var ctrls = this.node.childNodes[1];
          if (uiObject instanceof ORYX.Core.Controls.Docker) {
            parent = ctrls.childNodes[0];
            if (this.dockers.length >= 2) {
              this.dockers.splice(index !== undefined ? Math.min(index, this.dockers.length - 1) : this.dockers.length - 1, 0, uiObject);
            } else {
              this.dockers.push(uiObject);
            }
          } else if (uiObject instanceof ORYX.Core.Controls.Magnet) {
            parent = ctrls.childNodes[1];
            this.magnets.push(uiObject);
          } else {
            parent = ctrls;
          }
        } else {
          //UIObject
          parent = this.node;
        }

        if (index != undefined && index < parent.childNodes.length)
          uiObject.node = parent.insertBefore(uiObject.node, parent.childNodes[index]);
        else uiObject.node = parent.appendChild(uiObject.node);

        this._changed();
        //uiObject.bounds.registerCallback(this._changedCallback);

        if (this.eventHandlerCallback && silent !== true)
          this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEADDED, shape: uiObject });
      } else {
        ORYX.Log.warn('add: ORYX.Core.UIObject is already a child of this object.');
      }
    } else {
      ORYX.Log.warn('add: Parameter is not of type ORYX.Core.UIObject.');
    }
  },

  /**
   * Overrides the UIObject.remove method. Removes uiObject.
   * @param {UIObject} uiObject
   */
  remove: function (uiObject, silent) {
    //if uiObject is a child of this object, remove it.
    if (this.children.member(uiObject)) {
      //remove uiObject from children
      var parent = uiObject.parent;

      this.children = this.children.without(uiObject);

      //delete parent reference of uiObject
      uiObject.parent = undefined;

      //delete uiObject.node from this.node
      if (uiObject instanceof ORYX.Core.Shape) {
        if (uiObject instanceof ORYX.Core.Edge) {
          uiObject.removeMarkers();
          uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node);
        } else {
          uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node);
          this.nodes = this.nodes.without(uiObject);
        }
      } else if (uiObject instanceof ORYX.Core.Controls.Control) {
        if (uiObject instanceof ORYX.Core.Controls.Docker) {
          uiObject.node = this.node.childNodes[1].childNodes[0].removeChild(uiObject.node);
          this.dockers = this.dockers.without(uiObject);
        } else if (uiObject instanceof ORYX.Core.Controls.Magnet) {
          uiObject.node = this.node.childNodes[1].childNodes[1].removeChild(uiObject.node);
          this.magnets = this.magnets.without(uiObject);
        } else {
          uiObject.node = this.node.childNodes[1].removeChild(uiObject.node);
        }
      }

      if (this.eventHandlerCallback && silent !== true)
        this.eventHandlerCallback({ type: ORYX.CONFIG.EVENT_SHAPEREMOVED, shape: uiObject, parent: parent });

      this._changed();
      //uiObject.bounds.unregisterCallback(this._changedCallback);
    } else {
      ORYX.Log.warn('remove: ORYX.Core.UIObject is not a child of this object.');
    }
  },

  /**
   * Calculate the Border Intersection Point between two points
   * @param {PointA}
   * @param {PointB}
   */
  getIntersectionPoint: function () {
    var pointAX, pointAY, pointBX, pointBY;

    // Get the the two Points
    switch (arguments.length) {
      case 2:
        pointAX = arguments[0].x;
        pointAY = arguments[0].y;
        pointBX = arguments[1].x;
        pointBY = arguments[1].y;
        break;
      case 4:
        pointAX = arguments[0];
        pointAY = arguments[1];
        pointBX = arguments[2];
        pointBY = arguments[3];
        break;
      default:
        throw 'getIntersectionPoints needs two or four arguments';
    }

    // Defined an include and exclude point
    var includePointX, includePointY, excludePointX, excludePointY;

    var bounds = this.absoluteBounds();

    if (this.isPointIncluded(pointAX, pointAY, bounds)) {
      includePointX = pointAX;
      includePointY = pointAY;
    } else {
      excludePointX = pointAX;
      excludePointY = pointAY;
    }

    if (this.isPointIncluded(pointBX, pointBY, bounds)) {
      includePointX = pointBX;
      includePointY = pointBY;
    } else {
      excludePointX = pointBX;
      excludePointY = pointBY;
    }

    // If there is no inclue or exclude Shape, than return
    if (!includePointX || !includePointY || !excludePointX || !excludePointY) {
      return undefined;
    }

    var midPointX = 0;
    var midPointY = 0;

    var refPointX, refPointY;

    var minDifferent = 1;
    // Get the UpperLeft and LowerRight
    //var ul = bounds.upperLeft();
    //var lr = bounds.lowerRight();

    var i = 0;

    while (true) {
      // Calculate the midpoint of the current to points
      var midPointX =
        Math.min(includePointX, excludePointX) + (Math.max(includePointX, excludePointX) - Math.min(includePointX, excludePointX)) / 2.0;
      var midPointY =
        Math.min(includePointY, excludePointY) + (Math.max(includePointY, excludePointY) - Math.min(includePointY, excludePointY)) / 2.0;

      // Set the new midpoint by the means of the include of the bounds
      if (this.isPointIncluded(midPointX, midPointY, bounds)) {
        includePointX = midPointX;
        includePointY = midPointY;
      } else {
        excludePointX = midPointX;
        excludePointY = midPointY;
      }

      // Calc the length of the line
      var length = Math.sqrt(Math.pow(includePointX - excludePointX, 2) + Math.pow(includePointY - excludePointY, 2));
      // Calc a point one step from the include point
      (refPointX = includePointX + (excludePointX - includePointX) / length),
        (refPointY = includePointY + (excludePointY - includePointY) / length);

      // If the reference point not in the bounds, break
      if (!this.isPointIncluded(refPointX, refPointY, bounds)) {
        break;
      }
    }

    // Return the last includepoint
    return { x: refPointX, y: refPointY };
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   */
  isPointIncluded: function () {
    return false;
  },

  /**
   * Returns TRUE if the given node
   * is a child node of the shapes node
   * @param {Element} node
   * @return {Boolean}
   *
   */
  containsNode: function (node) {
    var me = this.node.firstChild.firstChild;
    while (node) {
      if (node == me) {
        return true;
      }
      node = node.parentNode;
    }
    return false;
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function () {
    return this.isPointIncluded.apply(this, arguments);
  },

  _dockerChanged: function () {},

  /**
   * Create a Docker for this Edge
   *
   */
  createDocker: function (index, position) {
    var docker = new ORYX.Core.Controls.Docker({ eventHandlerCallback: this.eventHandlerCallback });
    docker.bounds.registerCallback(this._dockerChangedCallback);
    if (position) {
      docker.bounds.centerMoveTo(position);
    }
    this.add(docker, index);

    return docker;
  },

  /**
   * Get the serialized object
   * return Array with hash-entrees (prefix, name, value)
   * Following values will given:
   * 		Bounds
   * 		Outgoing Shapes
   * 		Parent
   */
  serialize: function () {
    var serializedObject = arguments.callee.$.serialize.apply(this);

    // Add the bounds
    serializedObject.push({ name: 'bounds', prefix: 'oryx', value: this.bounds.serializeForERDF(), type: 'literal' });

    // Add the outgoing shapes
    this.getOutgoingShapes().each(
      function (followingShape) {
        serializedObject.push({
          name: 'outgoing',
          prefix: 'raziel',
          value: '#' + ERDF.__stripHashes(followingShape.resourceId),
          type: 'resource',
        });
      }.bind(this)
    );

    // Add the parent shape, if the parent not the canvas
    //if(this.parent instanceof ORYX.Core.Shape){
    serializedObject.push({ name: 'parent', prefix: 'raziel', value: '#' + ERDF.__stripHashes(this.parent.resourceId), type: 'resource' });
    //}

    return serializedObject;
  },

  deserialize: function (serialize, json) {
    arguments.callee.$.deserialize.apply(this, arguments);

    // Set the Bounds
    var bounds = serialize.find(function (ser) {
      return 'oryx-bounds' === ser.prefix + '-' + ser.name;
    });
    if (bounds) {
      var b = bounds.value.replace(/,/g, ' ').split(' ').without('');
      if (this instanceof ORYX.Core.Edge) {
        if (!this.dockers.first().isChanged) this.dockers.first().bounds.centerMoveTo(parseFloat(b[0]), parseFloat(b[1]));
        if (!this.dockers.last().isChanged) this.dockers.last().bounds.centerMoveTo(parseFloat(b[2]), parseFloat(b[3]));
      } else {
        this.bounds.set(parseFloat(b[0]), parseFloat(b[1]), parseFloat(b[2]), parseFloat(b[3]));
      }
    }

    if (json && json.labels instanceof Array) {
      json.labels.each(
        function (slabel) {
          var label = this.getLabel(slabel.ref);
          if (label) {
            label.deserialize(slabel, this);
          }
        }.bind(this)
      );
    }
  },

  toJSON: function () {
    var json = arguments.callee.$.toJSON.apply(this, arguments);

    var labels = [],
      id = this.id;
    this._labels.each(function (obj) {
      var slabel = obj.value.serialize();
      if (slabel) {
        slabel.ref = obj.key.replace(id, '');
        labels.push(slabel);
      }
    });

    if (labels.length > 0) {
      json.labels = labels;
    }
    return json;
  },

  /**
   * Private methods.
   */

  /**
   * Child classes have to overwrite this method for initializing a loaded
   * SVG representation.
   * @param {SVGDocument} svgDocument
   */
  _init: function (svgDocument) {
    //adjust ids
    this._adjustIds(svgDocument, 0);
  },

  _adjustIds: function (element, idIndex) {
    if (element instanceof Element) {
      var eid = element.getAttributeNS(null, 'id');
      if (eid && eid !== '') {
        element.setAttributeNS(null, 'id', this.id + eid);
      } else {
        element.setAttributeNS(null, 'id', this.id + '_' + this.id + '_' + idIndex);
        idIndex++;
      }

      // Replace URL in fill attribute
      var fill = element.getAttributeNS(null, 'fill');
      if (fill && fill.include('url(#')) {
        fill = fill.replace(/url\(#/g, 'url(#' + this.id);
        element.setAttributeNS(null, 'fill', fill);
      }

      if (element.hasChildNodes()) {
        for (var i = 0; i < element.childNodes.length; i++) {
          idIndex = this._adjustIds(element.childNodes[i], idIndex);
        }
      }
    }
    return idIndex;
  },

  toString: function () {
    return 'ORYX.Core.Shape ' + this.getId();
  },
};
ORYX.Core.Shape = ORYX.Core.AbstractShape.extend(ORYX.Core.Shape);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {};
}

/**
 * @classDescription Abstract base class for all Controls.
 */
ORYX.Core.Controls.Control = ORYX.Core.UIObject.extend({
  toString: function () {
    return 'Control ' + this.id;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {};
}

/**
 * @classDescription Represents a movable docker that can be bound to a shape. Dockers are used
 * for positioning shape objects.
 * @extends {Control}
 *
 * TODO absoluteXY und absoluteCenterXY von einem Docker liefern falsche Werte!!!
 */
ORYX.Core.Controls.Docker = ORYX.Core.Controls.Control.extend({
  /**
   * Constructor
   */
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    this.isMovable = true; // Enables movability
    this.bounds.set(0, 0, 16, 16); // Set the bounds
    this.referencePoint = undefined; // Refrenzpoint
    this._dockedShapeBounds = undefined;
    this._dockedShape = undefined;
    this._oldRefPoint1 = undefined;
    this._oldRefPoint2 = undefined;

    //this.anchors = [];
    this.anchorLeft;
    this.anchorRight;
    this.anchorTop;
    this.anchorBottom;

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, ['g']);

    // The DockerNode reprasentation
    this._dockerNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'g',
      { 'pointer-events': 'all' },
      ['circle', { cx: '8', cy: '8', r: '8', stroke: 'none', fill: 'none' }],
      ['circle', { cx: '8', cy: '8', r: '3', stroke: 'black', fill: 'red', 'stroke-width': '1' }],
    ]);

    // The ReferenzNode reprasentation
    this._referencePointNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'g',
      { 'pointer-events': 'none' },
      ['circle', { cx: this.bounds.upperLeft().x, cy: this.bounds.upperLeft().y, r: 3, fill: 'red', 'fill-opacity': 0.4 }],
    ]);

    // Hide the Docker
    this.hide();

    //Add to the EventHandler
    this.addEventHandlers(this._dockerNode);

    // Buffer the Update Callback for un-/register on Event-Handler
    this._updateCallback = this._changed.bind(this);
  },

  update: function () {
    // If there have an DockedShape
    if (this._dockedShape) {
      if (this._dockedShapeBounds && this._dockedShape instanceof ORYX.Core.Node) {
        // Calc the delta of width and height of the lastBounds and the current Bounds
        var dswidth = this._dockedShapeBounds.width();
        var dsheight = this._dockedShapeBounds.height();
        if (!dswidth) dswidth = 1;
        if (!dsheight) dsheight = 1;
        var widthDelta = this._dockedShape.bounds.width() / dswidth;
        var heightDelta = this._dockedShape.bounds.height() / dsheight;

        // If there is an different
        if (widthDelta !== 1.0 || heightDelta !== 1.0) {
          // Set the delta
          this.referencePoint.x *= widthDelta;
          this.referencePoint.y *= heightDelta;
        }

        // Clone these bounds
        this._dockedShapeBounds = this._dockedShape.bounds.clone();
      }

      // Get the first and the last Docker of the parent Shape
      var dockerIndex = this.parent.dockers.indexOf(this);
      var dock1 = this;
      var dock2 =
        this.parent.dockers.length > 1
          ? dockerIndex === 0 // If there is the first element
            ? this.parent.dockers[dockerIndex + 1] // then take the next docker
            : this.parent.dockers[dockerIndex - 1] // if not, then take the docker before
          : undefined;

      // Calculate the first absolute Refenzpoint
      var absoluteReferenzPoint1 = dock1.getDockedShape() ? dock1.getAbsoluteReferencePoint() : dock1.bounds.center();

      // Calculate the last absolute Refenzpoint
      var absoluteReferenzPoint2 =
        dock2 && dock2.getDockedShape() ? dock2.getAbsoluteReferencePoint() : dock2 ? dock2.bounds.center() : undefined;

      // If there is no last absolute Referenzpoint
      if (!absoluteReferenzPoint2) {
        // Calculate from the middle of the DockedShape
        var center = this._dockedShape.absoluteCenterXY();
        var minDimension = this._dockedShape.bounds.width() * this._dockedShape.bounds.height();
        absoluteReferenzPoint2 = {
          x: absoluteReferenzPoint1.x + (center.x - absoluteReferenzPoint1.x) * -minDimension,
          y: absoluteReferenzPoint1.y + (center.y - absoluteReferenzPoint1.y) * -minDimension,
        };
      }

      var newPoint = undefined;

      /*if (!this._oldRefPoint1 || !this._oldRefPoint2 ||
				absoluteReferenzPoint1.x !== this._oldRefPoint1.x ||
				absoluteReferenzPoint1.y !== this._oldRefPoint1.y ||
				absoluteReferenzPoint2.x !== this._oldRefPoint2.x ||
				absoluteReferenzPoint2.y !== this._oldRefPoint2.y) {*/

      // Get the new point for the Docker, calucalted by the intersection point of the Shape and the two points
      newPoint = this._dockedShape.getIntersectionPoint(absoluteReferenzPoint1, absoluteReferenzPoint2);

      // If there is new point, take the referencepoint as the new point
      if (!newPoint) {
        newPoint = this.getAbsoluteReferencePoint();
      }

      if (this.parent && this.parent.parent) {
        var grandParentPos = this.parent.parent.absoluteXY();
        newPoint.x -= grandParentPos.x;
        newPoint.y -= grandParentPos.y;
      }

      // Set the bounds to the new point
      this.bounds.centerMoveTo(newPoint);

      this._oldRefPoint1 = absoluteReferenzPoint1;
      this._oldRefPoint2 = absoluteReferenzPoint2;
    }
    /*else {
				newPoint = this.bounds.center();
			}*/

    //	}

    // Call the super class
    arguments.callee.$.update.apply(this, arguments);
  },

  /**
   * Calls the super class refresh method and updates the view of the docker.
   */
  refresh: function () {
    arguments.callee.$.refresh.apply(this, arguments);

    // Refresh the dockers node
    var p = this.bounds.upperLeft();
    this._dockerNode.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')');

    // Refresh the referencepoints node
    p = Object.clone(this.referencePoint);

    if (p && this._dockedShape) {
      var upL;
      if (this.parent instanceof ORYX.Core.Edge) {
        upL = this._dockedShape.absoluteXY();
      } else {
        upL = this._dockedShape.bounds.upperLeft();
      }
      p.x += upL.x;
      p.y += upL.y;
    } else {
      p = this.bounds.center();
    }

    this._referencePointNode.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')');
  },

  /**
   * Set the reference point
   * @param {Object} point
   */
  setReferencePoint: function (point) {
    // Set the referencepoint
    if (
      this.referencePoint !== point &&
      (!this.referencePoint || !point || this.referencePoint.x !== point.x || this.referencePoint.y !== point.y)
    ) {
      this.referencePoint = point;
      this._changed();
    }

    // Update directly, because the referencepoint has no influence of the bounds
    //this.refresh();
  },

  /**
   * Get the absolute referencepoint
   */
  getAbsoluteReferencePoint: function () {
    if (!this.referencePoint || !this._dockedShape) {
      return undefined;
    } else {
      var absUL = this._dockedShape.absoluteXY();
      return {
        x: this.referencePoint.x + absUL.x,
        y: this.referencePoint.y + absUL.y,
      };
    }
  },

  /**
   * Set the docked Shape from the docker
   * @param {Object} shape
   */
  setDockedShape: function (shape) {
    // If there is an old docked Shape
    if (this._dockedShape) {
      this._dockedShape.bounds.unregisterCallback(this._updateCallback);

      // Delete the Shapes from the incoming and outgoing array
      // If this Docker the incoming of the Shape
      if (this === this.parent.dockers.first()) {
        this.parent.incoming = this.parent.incoming.without(this._dockedShape);
        this._dockedShape.outgoing = this._dockedShape.outgoing.without(this.parent);

        // If this Docker the outgoing of the Shape
      } else if (this === this.parent.dockers.last()) {
        this.parent.outgoing = this.parent.outgoing.without(this._dockedShape);
        this._dockedShape.incoming = this._dockedShape.incoming.without(this.parent);
      }
    }

    // Set the new Shape
    this._dockedShape = shape;
    this._dockedShapeBounds = undefined;
    var referencePoint = undefined;

    // If there is an Shape, register the updateCallback if there are changes in the shape bounds
    if (this._dockedShape) {
      // Add the Shapes to the incoming and outgoing array
      // If this Docker the incoming of the Shape
      if (this === this.parent.dockers.first()) {
        this.parent.incoming.push(shape);
        shape.outgoing.push(this.parent);

        // If this Docker the outgoing of the Shape
      } else if (this === this.parent.dockers.last()) {
        this.parent.outgoing.push(shape);
        shape.incoming.push(this.parent);
      }

      // Get the bounds and set the new referencepoint
      var bounds = this.bounds;
      var absUL = shape.absoluteXY();

      /*if(shape.parent){
				var b = shape.parent.bounds.upperLeft();
				absUL.x -= b.x;
				absUL.y -= b.y;
			}*/

      referencePoint = {
        x: bounds.center().x - absUL.x,
        y: bounds.center().y - absUL.y,
      };

      this._dockedShapeBounds = this._dockedShape.bounds.clone();

      this._dockedShape.bounds.registerCallback(this._updateCallback);

      // Set the color of the docker as docked
      this.setDockerColor(ORYX.CONFIG.DOCKER_DOCKED_COLOR);
    } else {
      // Set the color of the docker as undocked
      this.setDockerColor(ORYX.CONFIG.DOCKER_UNDOCKED_COLOR);
    }

    // Set the referencepoint
    this.setReferencePoint(referencePoint);
    this._changed();
    //this.update();
  },

  /**
   * Get the docked Shape
   */
  getDockedShape: function () {
    return this._dockedShape;
  },

  /**
   * Returns TRUE if the docker has a docked shape
   */
  isDocked: function () {
    return !!this._dockedShape;
  },

  /**
   * Set the Color of the Docker
   * @param {Object} color
   */
  setDockerColor: function (color) {
    this._dockerNode.lastChild.setAttributeNS(null, 'fill', color);
  },

  preventHiding: function (prevent) {
    this._preventHiding = Math.max(0, (this._preventHiding || 0) + (prevent ? 1 : -1));
  },

  /**
   * Hides this UIObject and all its children.
   */
  hide: function () {
    if (this._preventHiding) {
      return false;
    }

    // Hide docker and reference point
    this.node.setAttributeNS(null, 'visibility', 'hidden');
    this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden');

    this.children.each(function (uiObj) {
      uiObj.hide();
    });
  },

  /**
   * Enables visibility of this UIObject and all its children.
   */
  show: function () {
    // Show docker
    this.node.setAttributeNS(null, 'visibility', 'visible');

    // Hide reference point if the connected shape is an edge
    if (this.getDockedShape() instanceof ORYX.Core.Edge) {
      this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden');
    } else {
      this._referencePointNode.setAttributeNS(null, 'visibility', 'visible');
    }

    this.children.each(function (uiObj) {
      uiObj.show();
    });
  },

  toString: function () {
    return 'Docker ' + this.id;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}
if (!ORYX.Core.Controls) {
  ORYX.Core.Controls = {};
}

/**
 * @classDescription Represents a magnet that is part of another shape and can
 * be attached to dockers. Magnets are used for linking edge objects
 * to other Shape objects.
 * @extends {Control}
 */
ORYX.Core.Controls.Magnet = ORYX.Core.Controls.Control.extend({
  /**
   * Constructor
   */
  construct: function () {
    arguments.callee.$.construct.apply(this, arguments);

    //this.anchors = [];
    this.anchorLeft;
    this.anchorRight;
    this.anchorTop;
    this.anchorBottom;

    this.bounds.set(0, 0, 16, 16);

    //graft magnet's root node into owner's control group.
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'g',
      { 'pointer-events': 'all' },
      ['circle', { cx: '8', cy: '8', r: '4', stroke: 'none', fill: 'red', 'fill-opacity': '0.3' }],
    ]);

    this.hide();
  },

  update: function () {
    arguments.callee.$.update.apply(this, arguments);

    //this.isChanged = true;
  },

  _update: function () {
    arguments.callee.$.update.apply(this, arguments);

    //this.isChanged = true;
  },

  refresh: function () {
    arguments.callee.$.refresh.apply(this, arguments);

    var p = this.bounds.upperLeft();
    /*if(this.parent) {
			var parentPos = this.parent.bounds.upperLeft();
			p.x += parentPos.x;
			p.y += parentPos.y;
		}*/

    this.node.setAttributeNS(null, 'transform', 'translate(' + p.x + ', ' + p.y + ')');
  },

  show: function () {
    //this.refresh();
    arguments.callee.$.show.apply(this, arguments);
  },

  toString: function () {
    return 'Magnet ' + this.id;
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription Abstract base class for all Nodes.
 * @extends ORYX.Core.Shape
 */
ORYX.Core.Node = {
  /**
   * Constructor
   * @param options {Object} A container for arguments.
   * @param stencil {Stencil}
   */
  construct: function (options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments);

    this.isSelectable = true;
    this.isMovable = true;
    this._dockerUpdated = false;
    this.facade = facade;

    this._oldBounds = new ORYX.Core.Bounds(); //init bounds with undefined values
    this._svgShapes = []; //array of all SVGShape objects of
    // SVG representation

    //TODO vielleicht in shape verschieben?
    this.minimumSize = undefined; // {width:..., height:...}
    this.maximumSize = undefined;

    //TODO vielleicht in shape oder uiobject verschieben?
    // vielleicht sogar isResizable ersetzen?
    this.isHorizontallyResizable = false;
    this.isVerticallyResizable = false;

    this.dataId = undefined;

    this._init(this._stencil.view());
    this.forcedHeight = -1;
  },

  /**
   * This method checks whether the shape is resized correctly and calls the
   * super class update method.
   */
  _update: function () {
    this.dockers.invoke('update');
    if (this.isChanged) {
      var bounds = this.bounds;
      var oldBounds = this._oldBounds;

      if (this.isResized) {
        var widthDelta = bounds.width() / oldBounds.width();
        var heightDelta = bounds.height() / oldBounds.height();

        //iterate over all relevant svg elements and resize them
        this._svgShapes.each(function (svgShape) {
          //adjust width
          if (svgShape.isHorizontallyResizable) {
            svgShape.width = svgShape.oldWidth * widthDelta;
          }
          //adjust height
          if (svgShape.isVerticallyResizable) {
            svgShape.height = svgShape.oldHeight * heightDelta;
          }

          //check, if anchors are set
          var anchorOffset;
          var leftIncluded = svgShape.anchorLeft;
          var rightIncluded = svgShape.anchorRight;

          if (rightIncluded) {
            anchorOffset = oldBounds.width() - (svgShape.oldX + svgShape.oldWidth);
            if (leftIncluded) {
              svgShape.width = bounds.width() - svgShape.x - anchorOffset;
            } else {
              svgShape.x = bounds.width() - (anchorOffset + svgShape.width);
            }
          } else if (!leftIncluded) {
            svgShape.x = widthDelta * svgShape.oldX;
            if (!svgShape.isHorizontallyResizable) {
              svgShape.x = svgShape.x + (svgShape.width * widthDelta) / 2 - svgShape.width / 2;
            }
          }

          var topIncluded = svgShape.anchorTop;
          var bottomIncluded = svgShape.anchorBottom;

          if (bottomIncluded) {
            anchorOffset = oldBounds.height() - (svgShape.oldY + svgShape.oldHeight);
            if (topIncluded) {
              svgShape.height = bounds.height() - svgShape.y - anchorOffset;
            } else {
              // Hack for choreography task layouting
              if (!svgShape._isYLocked) {
                svgShape.y = bounds.height() - (anchorOffset + svgShape.height);
              }
            }
          } else if (!topIncluded) {
            svgShape.y = heightDelta * svgShape.oldY;
            if (!svgShape.isVerticallyResizable) {
              svgShape.y = svgShape.y + (svgShape.height * heightDelta) / 2 - svgShape.height / 2;
            }
          }
        });

        //check, if the current bounds is unallowed horizontally or vertically resized
        var p = {
          x: 0,
          y: 0,
        };
        if (!this.isHorizontallyResizable && bounds.width() !== oldBounds.width()) {
          p.x = oldBounds.width() - bounds.width();
        }
        if (!this.isVerticallyResizable && bounds.height() !== oldBounds.height()) {
          p.y = oldBounds.height() - bounds.height();
        }
        if (p.x !== 0 || p.y !== 0) {
          bounds.extend(p);
        }

        //check, if the current bounds are between maximum and minimum bounds
        p = {
          x: 0,
          y: 0,
        };
        var widthDifference, heightDifference;
        if (this.minimumSize) {
          ORYX.Log.debug("Shape (%0)'s min size: (%1x%2)", this, this.minimumSize.width, this.minimumSize.height);
          widthDifference = this.minimumSize.width - bounds.width();
          if (widthDifference > 0) {
            p.x += widthDifference;
          }
          heightDifference = this.minimumSize.height - bounds.height();
          if (heightDifference > 0) {
            p.y += heightDifference;
          }
        }
        if (this.maximumSize) {
          ORYX.Log.debug("Shape (%0)'s max size: (%1x%2)", this, this.maximumSize.width, this.maximumSize.height);
          widthDifference = bounds.width() - this.maximumSize.width;
          if (widthDifference > 0) {
            p.x -= widthDifference;
          }
          heightDifference = bounds.height() - this.maximumSize.height;
          if (heightDifference > 0) {
            p.y -= heightDifference;
          }
        }
        if (p.x !== 0 || p.y !== 0) {
          bounds.extend(p);
        }

        //update magnets

        var widthDelta = bounds.width() / oldBounds.width();
        var heightDelta = bounds.height() / oldBounds.height();

        var leftIncluded, rightIncluded, topIncluded, bottomIncluded, center, newX, newY;

        this.magnets.each(function (magnet) {
          leftIncluded = magnet.anchorLeft;
          rightIncluded = magnet.anchorRight;
          topIncluded = magnet.anchorTop;
          bottomIncluded = magnet.anchorBottom;

          center = magnet.bounds.center();

          if (leftIncluded) {
            newX = center.x;
          } else if (rightIncluded) {
            newX = bounds.width() - (oldBounds.width() - center.x);
          } else {
            newX = center.x * widthDelta;
          }

          if (topIncluded) {
            newY = center.y;
          } else if (bottomIncluded) {
            newY = bounds.height() - (oldBounds.height() - center.y);
          } else {
            newY = center.y * heightDelta;
          }

          if (center.x !== newX || center.y !== newY) {
            magnet.bounds.centerMoveTo(newX, newY);
          }
        });

        //set new position of labels
        this.getLabels().each(function (label) {
          // Set the position dependings on it anchor
          if (!label.isAnchorLeft()) {
            if (label.isAnchorRight()) {
              label.setX(bounds.width() - (oldBounds.width() - label.oldX));
            } else {
              label.setX((label.position ? label.position.x : label.x) * widthDelta);
            }
          }
          if (!label.isAnchorTop()) {
            if (label.isAnchorBottom()) {
              label.setY(bounds.height() - (oldBounds.height() - label.oldY));
            } else {
              label.setY((label.position ? label.position.y : label.y) * heightDelta);
            }
          }

          // If there is an position,
          // set the origin position as well
          if (label.position) {
            if (!label.isOriginAnchorLeft()) {
              if (label.isOriginAnchorRight()) {
                label.setOriginX(bounds.width() - (oldBounds.width() - label.oldX));
              } else {
                label.setOriginX(label.x * widthDelta);
              }
            }
            if (!label.isOriginAnchorTop()) {
              if (label.isOriginAnchorBottom()) {
                label.setOriginY(bounds.height() - (oldBounds.height() - label.oldY));
              } else {
                label.setOriginY(label.y * heightDelta);
              }
            }
          }
        });

        //update docker
        var docker = this.dockers[0];
        if (docker) {
          docker.bounds.unregisterCallback(this._dockerChangedCallback);
          if (!this._dockerUpdated) {
            docker.bounds.centerMoveTo(this.bounds.center());
            this._dockerUpdated = false;
          }

          docker.update();
          docker.bounds.registerCallback(this._dockerChangedCallback);
        }
        this.isResized = false;
      }

      this.refresh();

      this.isChanged = false;

      this._oldBounds = this.bounds.clone();
    }

    this.children.each(function (value) {
      if (!(value instanceof ORYX.Core.Controls.Docker)) {
        value._update();
      }
    });

    if (this.dockers.length > 0 && !this.dockers.first().getDockedShape()) {
      this.dockers.each(
        function (docker) {
          docker.bounds.centerMoveTo(this.bounds.center());
        }.bind(this)
      );
    }

    /*this.incoming.each((function(edge) {
			if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node))
				edge._update(true);
		}).bind(this));
		
		this.outgoing.each((function(edge) {
			if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node))
				edge._update(true);
		}).bind(this)); */
  },

  /**
   * This method repositions and resizes the SVG representation
   * of the shape.
   */
  refresh: function () {
    arguments.callee.$.refresh.apply(this, arguments);

    /** Movement */
    var x = this.bounds.upperLeft().x;
    var y = this.bounds.upperLeft().y;

    // Move owner element
    this.node.firstChild.setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')');
    // Move magnets
    this.node.childNodes[1].childNodes[1].setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')');

    /** Resize */

    //iterate over all relevant svg elements and update them
    this._svgShapes.each(function (svgShape) {
      svgShape.update();
    });
  },

  _dockerChanged: function () {
    var docker = this.dockers[0];

    //set the bounds of the the association
    this.bounds.centerMoveTo(docker.bounds.center());

    this._dockerUpdated = true;
    //this._update(true);
  },

  /**
   * This method traverses a tree of SVGElements and returns
   * all SVGShape objects. For each basic shape or path element
   * a SVGShape object is initialized.
   *
   * @param svgNode {SVGElement}
   * @return {Array} Array of SVGShape objects
   */
  _initSVGShapes: function (svgNode) {
    var svgShapes = [];
    try {
      var svgShape = new ORYX.Core.SVG.SVGShape(svgNode);
      svgShapes.push(svgShape);
    } catch (e) {
      //do nothing
    }

    if (svgNode.hasChildNodes()) {
      for (var i = 0; i < svgNode.childNodes.length; i++) {
        svgShapes = svgShapes.concat(this._initSVGShapes(svgNode.childNodes[i]));
      }
    }

    return svgShapes;
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   * @param {absoluteBounds} optional: for performance
   */
  isPointIncluded: function (pointX, pointY, absoluteBounds) {
    // If there is an arguments with the absoluteBounds
    var absBounds = absoluteBounds && absoluteBounds instanceof ORYX.Core.Bounds ? absoluteBounds : this.absoluteBounds();

    if (!absBounds.isIncluded(pointX, pointY)) {
      return false;
    } else {
    }

    //point = Object.clone(point);
    var ul = absBounds.upperLeft();
    var x = pointX - ul.x;
    var y = pointY - ul.y;

    var i = 0;
    do {
      var isPointIncluded = this._svgShapes[i++].isPointIncluded(x, y);
    } while (!isPointIncluded && i < this._svgShapes.length);

    return isPointIncluded;

    /*return this._svgShapes.any(function(svgShape){
            return svgShape.isPointIncluded(point);
        });*/
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function (pointX, pointY) {
    var isOverEl = arguments.callee.$.isPointOverOffset.apply(this, arguments);

    if (isOverEl) {
      // If there is an arguments with the absoluteBounds
      var absBounds = this.absoluteBounds();
      absBounds.widen(-ORYX.CONFIG.BORDER_OFFSET);

      if (!absBounds.isIncluded(pointX, pointY)) {
        return true;
      }
    }

    return false;
  },

  serialize: function () {
    var result = arguments.callee.$.serialize.apply(this);

    // Add the docker's bounds
    // nodes only have at most one docker!
    this.dockers.each(
      function (docker) {
        if (docker.getDockedShape()) {
          var center = docker.referencePoint;
          center = center ? center : docker.bounds.center();
          result.push({
            name: 'docker',
            prefix: 'oryx',
            value: $H(center).values().join(','),
            type: 'literal',
          });
        }
      }.bind(this)
    );

    // Get the spezific serialized object from the stencil
    try {
      //result = this.getStencil().serialize(this, result);

      var serializeEvent = this.getStencil().serialize();

      /*
       * call serialize callback by reference, result should be found
       * in serializeEvent.result
       */
      if (serializeEvent.type) {
        serializeEvent.shape = this;
        serializeEvent.data = result;
        serializeEvent.result = undefined;
        serializeEvent.forceExecution = true;

        this._delegateEvent(serializeEvent);

        if (serializeEvent.result) {
          result = serializeEvent.result;
        }
      }
    } catch (e) {}
    return result;
  },

  deserialize: function (data) {
    arguments.callee.$.deserialize.apply(this, arguments);

    try {
      //data = this.getStencil().deserialize(this, data);

      var deserializeEvent = this.getStencil().deserialize();

      /*
       * call serialize callback by reference, result should be found
       * in serializeEventInfo.result
       */
      if (deserializeEvent.type) {
        deserializeEvent.shape = this;
        deserializeEvent.data = data;
        deserializeEvent.result = undefined;
        deserializeEvent.forceExecution = true;

        this._delegateEvent(deserializeEvent);
        if (deserializeEvent.result) {
          data = deserializeEvent.result;
        }
      }
    } catch (e) {}

    // Set the outgoing shapes
    var outgoing = data.findAll(function (ser) {
      return ser.prefix + '-' + ser.name == 'raziel-outgoing';
    });
    outgoing.each(
      function (obj) {
        // TODO: Look at Canvas
        if (!this.parent) {
          return;
        }

        // Set outgoing Shape
        var next = this.getCanvas().getChildShapeByResourceId(obj.value);

        if (next) {
          if (next instanceof ORYX.Core.Edge) {
            //Set the first docker of the next shape
            next.dockers.first().setDockedShape(this);
            next.dockers.first().setReferencePoint(next.dockers.first().bounds.center());
          } else if (next.dockers.length > 0) {
            //next is a node and next has a docker
            next.dockers.first().setDockedShape(this);
            //next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
          }
        }
      }.bind(this)
    );

    if (this.dockers.length === 1) {
      var dockerPos;
      dockerPos = data.find(function (entry) {
        return entry.prefix + '-' + entry.name === 'oryx-dockers';
      });

      if (dockerPos) {
        var points = dockerPos.value.replace(/,/g, ' ').split(' ').without('').without('#');
        if (points.length === 2 && this.dockers[0].getDockedShape()) {
          this.dockers[0].setReferencePoint({
            x: parseFloat(points[0]),
            y: parseFloat(points[1]),
          });
        } else {
          this.dockers[0].bounds.centerMoveTo(parseFloat(points[0]), parseFloat(points[1]));
        }
      }
    }
  },

  /**
   * This method excepts the SVGDoucment that is the SVG representation
   * of this shape.
   * The bounds of the shape are calculated, the SVG representation's upper left point
   * is moved to 0,0 and it the method sets if this shape is resizable.
   *
   * @param {SVGDocument} svgDocument
   */
  _init: function (svgDocument) {
    arguments.callee.$._init.apply(this, arguments);

    var svgNode = svgDocument.getElementsByTagName('g')[0]; //outer most g node
    // set all required attributes
    var attributeTitle = svgDocument.ownerDocument.createAttribute('title');
    attributeTitle.nodeValue = this.getStencil().title();
    svgNode.setAttributeNode(attributeTitle);

    var attributeId = svgDocument.ownerDocument.createAttribute('id');
    attributeId.nodeValue = this.id;
    svgNode.setAttributeNode(attributeId);

    //
    var stencilTargetNode = this.node.childNodes[0].childNodes[0]; //<g class=me>"
    svgNode = stencilTargetNode.appendChild(svgNode);

    // Add to the EventHandler
    this.addEventHandlers(svgNode.parentNode);

    /**set minimum and maximum size*/
    var minSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'minimumSize');
    if (minSizeAttr) {
      minSizeAttr = minSizeAttr.replace('/,/g', ' ');
      var minSizeValues = minSizeAttr.split(' ');
      minSizeValues = minSizeValues.without('');

      if (minSizeValues.length > 1) {
        this.minimumSize = {
          width: parseFloat(minSizeValues[0]),
          height: parseFloat(minSizeValues[1]),
        };
      } else {
        //set minimumSize to (1,1), so that width and height of the stencil can never be (0,0)
        this.minimumSize = {
          width: 1,
          height: 1,
        };
      }
    }

    var maxSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'maximumSize');
    if (maxSizeAttr) {
      maxSizeAttr = maxSizeAttr.replace('/,/g', ' ');
      var maxSizeValues = maxSizeAttr.split(' ');
      maxSizeValues = maxSizeValues.without('');

      if (maxSizeValues.length > 1) {
        this.maximumSize = {
          width: parseFloat(maxSizeValues[0]),
          height: parseFloat(maxSizeValues[1]),
        };
      }
    }

    if (
      this.minimumSize &&
      this.maximumSize &&
      (this.minimumSize.width > this.maximumSize.width || this.minimumSize.height > this.maximumSize.height)
    ) {
      //TODO wird verschluckt!!!
      throw this + ': Minimum Size must be greater than maxiumSize.';
    }

    /**get current bounds and adjust it to upperLeft == (0,0)*/
    //initialize all SVGShape objects
    this._svgShapes = this._initSVGShapes(svgNode);

    //get upperLeft and lowerRight of stencil
    var upperLeft = {
      x: undefined,
      y: undefined,
    };
    var lowerRight = {
      x: undefined,
      y: undefined,
    };
    var me = this;
    this._svgShapes.each(function (svgShape) {
      upperLeft.x = upperLeft.x !== undefined ? Math.min(upperLeft.x, svgShape.x) : svgShape.x;
      upperLeft.y = upperLeft.y !== undefined ? Math.min(upperLeft.y, svgShape.y) : svgShape.y;
      lowerRight.x = lowerRight.x !== undefined ? Math.max(lowerRight.x, svgShape.x + svgShape.width) : svgShape.x + svgShape.width;
      lowerRight.y = lowerRight.y !== undefined ? Math.max(lowerRight.y, svgShape.y + svgShape.height) : svgShape.y + svgShape.height;

      /** set if resizing is enabled */
      //TODO isResizable durch die beiden anderen booleans ersetzen?
      if (svgShape.isHorizontallyResizable) {
        me.isHorizontallyResizable = true;
        me.isResizable = true;
      }
      if (svgShape.isVerticallyResizable) {
        me.isVerticallyResizable = true;
        me.isResizable = true;
      }
      if (svgShape.anchorTop && svgShape.anchorBottom) {
        me.isVerticallyResizable = true;
        me.isResizable = true;
      }
      if (svgShape.anchorLeft && svgShape.anchorRight) {
        me.isHorizontallyResizable = true;
        me.isResizable = true;
      }
    });

    //move all SVGShapes by -upperLeft
    this._svgShapes.each(function (svgShape) {
      svgShape.x -= upperLeft.x;
      svgShape.y -= upperLeft.y;
      svgShape.update();
    });

    //set bounds of shape
    //the offsets are also needed for positioning the magnets and the docker
    var offsetX = upperLeft.x;
    var offsetY = upperLeft.y;

    lowerRight.x -= offsetX;
    lowerRight.y -= offsetY;
    upperLeft.x = 0;
    upperLeft.y = 0;

    //prevent that width or height of initial bounds is 0
    if (lowerRight.x === 0) {
      lowerRight.x = 1;
    }
    if (lowerRight.y === 0) {
      lowerRight.y = 1;
    }

    this._oldBounds.set(upperLeft, lowerRight);
    this.bounds.set(upperLeft, lowerRight);

    /**initialize magnets */

    var magnets = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'magnets');

    if (magnets && magnets.length > 0) {
      magnets = $A(magnets[0].getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'magnet'));

      var me = this;
      magnets.each(function (magnetElem) {
        var magnet = new ORYX.Core.Controls.Magnet({
          eventHandlerCallback: me.eventHandlerCallback,
        });
        var cx = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cx'));
        var cy = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cy'));
        magnet.bounds.centerMoveTo({
          x: cx - offsetX,
          y: cy - offsetY,
        });

        //get anchors
        var anchors = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors');
        if (anchors) {
          anchors = anchors.replace('/,/g', ' ');
          anchors = anchors.split(' ').without('');
          for (var i = 0; i < anchors.length; i++) {
            switch (anchors[i].toLowerCase()) {
              case 'left':
                magnet.anchorLeft = true;
                break;
              case 'right':
                magnet.anchorRight = true;
                break;
              case 'top':
                magnet.anchorTop = true;
                break;
              case 'bottom':
                magnet.anchorBottom = true;
                break;
            }
          }
        }

        me.add(magnet);

        //check, if magnet is default magnet
        if (!this._defaultMagnet) {
          var defaultAttr = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'default');
          if (defaultAttr && defaultAttr.toLowerCase() === 'yes') {
            me._defaultMagnet = magnet;
          }
        }
      });
    } else {
      // Add a Magnet in the Center of Shape
      var magnet = new ORYX.Core.Controls.Magnet();
      magnet.bounds.centerMoveTo(this.bounds.width() / 2, this.bounds.height() / 2);
      this.add(magnet);
    }

    /**initialize docker */
    var dockerElem = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, 'docker');

    if (dockerElem && dockerElem.length > 0) {
      dockerElem = dockerElem[0];
      var docker = this.createDocker();
      var cx = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cx'));
      var cy = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'cy'));
      docker.bounds.centerMoveTo({
        x: cx - offsetX,
        y: cy - offsetY,
      });

      //get anchors
      var anchors = dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'anchors');
      if (anchors) {
        anchors = anchors.replace('/,/g', ' ');
        anchors = anchors.split(' ').without('');

        for (var i = 0; i < anchors.length; i++) {
          switch (anchors[i].toLowerCase()) {
            case 'left':
              docker.anchorLeft = true;
              break;
            case 'right':
              docker.anchorRight = true;
              break;
            case 'top':
              docker.anchorTop = true;
              break;
            case 'bottom':
              docker.anchorBottom = true;
              break;
          }
        }
      }
    }

    /**initialize labels*/
    var textElems = svgNode.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text');
    $A(textElems).each(
      function (textElem) {
        var label = new ORYX.Core.SVG.Label({
          textElement: textElem,
          shapeId: this.id,
        });
        label.x -= offsetX;
        label.y -= offsetY;
        this._labels[label.id] = label;

        label.registerOnChange(this.layout.bind(this));

        // Only apply fitting on form-components
        if (this._stencil.id().indexOf(ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX) == 0) {
          label.registerOnChange(this.fitToLabels.bind(this));
        }
      }.bind(this)
    );
  },

  fitToLabels: function () {
    var y = 0;

    this.getLabels().each(function (label) {
      var lr = label.getY() + label.getHeight();
      if (lr > y) {
        y = lr;
      }
    });

    var bounds = this.bounds;
    var boundsChanged = false;

    if (this.minimumSize) {
      // Check if y-value exceeds the min-value. If not, stick to this value.
      var minHeight = this.minimumSize.height;
      if (y < minHeight && bounds.height() > minHeight && minHeight > this.forcedHeight) {
        bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + minHeight);
        boundsChanged = true;
      } else if (y > minHeight && bounds.height() != y && y > this.forcedHeight) {
        bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + y);
        boundsChanged = true;
      } else if (bounds.height() > this.forcedHeight && this.forcedHeight > 0) {
        bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + this.forcedHeight);
        boundsChanged = true;
      }
    }

    if (boundsChanged) {
      // Force facade to re-layout since bounds are changed AFTER layout has been performed
      if (this.facade.getCanvas() != null) {
        this.facade.getCanvas().update();
      }

      // Re-select if needed to force the select
      if (this.facade.getSelection().member(this)) {
        var selectedNow = this.facade.getSelection();
        this.facade.setSelection([]);
        this.facade.setSelection(selectedNow);
      }
    }
  },

  /**
   * Override the Method, that a docker is not shown
   *
   */
  createDocker: function () {
    var docker = new ORYX.Core.Controls.Docker({ eventHandlerCallback: this.eventHandlerCallback });
    docker.bounds.registerCallback(this._dockerChangedCallback);

    this.dockers.push(docker);
    docker.parent = this;
    docker.bounds.registerCallback(this._changedCallback);

    return docker;
  },

  toString: function () {
    return this._stencil.title() + ' ' + this.id;
  },
};
ORYX.Core.Node = ORYX.Core.Shape.extend(ORYX.Core.Node);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

NAMESPACE_SVG = 'http://www.w3.org/2000/svg';
NAMESPACE_ORYX = 'http://www.b3mn.org/oryx';

/**
 * Init namespaces
 */
if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Core) {
  ORYX.Core = {};
}

/**
 * @classDescription Abstract base class for all connections.
 * @extends {ORYX.Core.Shape}
 * @param options {Object}
 *
 * TODO da die verschiebung der Edge nicht ueber eine
 *  translation gemacht wird, die sich auch auf alle kind UIObjects auswirkt,
 *  muessen die kinder hier beim verschieben speziell betrachtet werden.
 *  Das sollte ueberarbeitet werden.
 *
 */
ORYX.Core.Edge = {
  /**
   * Constructor
   * @param {Object} options
   * @param {Stencil} stencil
   */
  construct: function (options, stencil, facade) {
    arguments.callee.$.construct.apply(this, arguments);

    this.isMovable = true;
    this.isSelectable = true;

    this._dockerUpdated = false;

    this._markers = new Hash(); //a hash map of SVGMarker objects where keys are the marker ids
    this._paths = [];
    this._interactionPaths = [];
    this._dockersByPath = new Hash();
    this._markersByPath = new Hash();

    /* Data structures to store positioning information of attached child nodes */
    this.attachedNodePositionData = new Hash();

    //TODO was muss hier initial erzeugt werden?
    var stencilNode = this.node.childNodes[0].childNodes[0];
    stencilNode = ORYX.Editor.graft('http://www.w3.org/2000/svg', stencilNode, [
      'g',
      {
        'pointer-events': 'painted',
      },
    ]);

    //Add to the EventHandler
    this.addEventHandlers(stencilNode.parentNode);

    this._oldBounds = this.bounds.clone();

    //load stencil
    this._init(this._stencil.view());

    if (stencil instanceof Array) {
      this.deserialize(stencil);
    }
  },

  _update: function (force) {
    if (this._dockerUpdated || this.isChanged || force) {
      this.dockers.invoke('update');

      if (false && (this.bounds.width() === 0 || this.bounds.height() === 0)) {
        var width = this.bounds.width();
        var height = this.bounds.height();
        this.bounds.extend({
          x: width === 0 ? 2 : 0,
          y: height === 0 ? 2 : 0,
        });
        this.bounds.moveBy({
          x: width === 0 ? -1 : 0,
          y: height === 0 ? -1 : 0,
        });
      }

      // TODO: Bounds muss abhaengig des Eltern-Shapes gesetzt werden
      var upL = this.bounds.upperLeft();
      var oldUpL = this._oldBounds.upperLeft();
      var oldWidth = this._oldBounds.width() === 0 ? this.bounds.width() : this._oldBounds.width();
      var oldHeight = this._oldBounds.height() === 0 ? this.bounds.height() : this._oldBounds.height();
      var diffX = upL.x - oldUpL.x;
      var diffY = upL.y - oldUpL.y;
      var diffWidth = this.bounds.width() / oldWidth || 1;
      var diffHeight = this.bounds.height() / oldHeight || 1;

      this.dockers.each(
        function (docker) {
          // Unregister on BoundsChangedCallback
          docker.bounds.unregisterCallback(this._dockerChangedCallback);

          // If there is any changes at the edge and is there is not an DockersUpdate
          // set the new bounds to the docker
          if (!this._dockerUpdated) {
            docker.bounds.moveBy(diffX, diffY);

            if (diffWidth !== 1 || diffHeight !== 1) {
              var relX = docker.bounds.upperLeft().x - upL.x;
              var relY = docker.bounds.upperLeft().y - upL.y;

              docker.bounds.moveTo(upL.x + relX * diffWidth, upL.y + relY * diffHeight);
            }
          }
          // Do Docker update and register on DockersBoundChange
          docker.update();
          docker.bounds.registerCallback(this._dockerChangedCallback);
        }.bind(this)
      );

      if (this._dockerUpdated) {
        var a = this.dockers.first().bounds.center();
        var b = this.dockers.first().bounds.center();

        this.dockers.each(
          function (docker) {
            var center = docker.bounds.center();
            a.x = Math.min(a.x, center.x);
            a.y = Math.min(a.y, center.y);
            b.x = Math.max(b.x, center.x);
            b.y = Math.max(b.y, center.y);
          }.bind(this)
        );

        //set the bounds of the the association
        this.bounds.set(Object.clone(a), Object.clone(b));
      }

      upL = this.bounds.upperLeft();
      oldUpL = this._oldBounds.upperLeft();
      diffWidth = this.bounds.width() / (oldWidth || this.bounds.width());
      diffHeight = this.bounds.height() / (oldHeight || this.bounds.height());
      diffX = upL.x - oldUpL.x;
      diffY = upL.y - oldUpL.y;

      //reposition labels
      this.getLabels().each(
        function (label) {
          if (label.getReferencePoint()) {
            var ref = label.getReferencePoint();
            var from = ref.segment.from,
              to = ref.segment.to;
            if (!from || !from.parent || !to || !to.parent) {
              return;
            }

            var fromPosition = from.bounds.center(),
              toPosition = to.bounds.center();

            if (
              fromPosition.x === ref.segment.fromPosition.x &&
              fromPosition.y === ref.segment.fromPosition.y &&
              toPosition.x === ref.segment.toPosition.x &&
              toPosition.y === ref.segment.toPosition.y &&
              !ref.dirty
            ) {
              return;
            }

            if (!this.parent.initializingShapes) {
              var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(
                ref.segment.fromPosition,
                ref.segment.toPosition,
                ref.intersection
              );
              var newIntersection = ORYX.Core.Math.getPointBetweenTwoPoints(
                fromPosition,
                toPosition,
                isNaN(oldDistance) ? 0.5 : oldDistance
              );

              /**
               * Set position
               */
              // Get the orthogonal identity vector of the current segment
              var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition);
              var isHor = Math.abs(oiv.y) === 1,
                isVer = Math.abs(oiv.x) === 1;
              oiv.x *= ref.distance;
              oiv.y *= ref.distance; // vector * distance
              oiv.x += newIntersection.x;
              oiv.y += newIntersection.y; // vector + the intersection point
              var mx = isHor && ref.orientation && (ref.iorientation || ref.orientation).endsWith('r') ? -label.getWidth() : 0;
              var my = isVer && ref.orientation && (ref.iorientation || ref.orientation).startsWith('l') ? -label.getHeight() + 2 : 0;
              label.setX(oiv.x + mx);
              label.setY(oiv.y + my);

              // Update the reference point
              this.updateReferencePointOfLabel(label, newIntersection, from, to);
            } else {
              var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition);
              oiv.x *= ref.distance;
              oiv.y *= ref.distance; // vector * distance
              oiv.x += ref.intersection.x;
              oiv.y += ref.intersection.y; // vector + the intersection point
              label.setX(oiv.x);
              label.setY(oiv.y);
              ref.segment.fromPosition = fromPosition;
              ref.segment.toPosition = toPosition;
            }

            return;
          }

          // Update label position if no reference point is set
          if (label.position && !this.parent.initializingShapes) {
            var x = label.position.x + diffX * (diffWidth || 1);
            if (x > this.bounds.lowerRight().x) {
              x += this.bounds.width() - this.bounds.width() / (diffWidth || 1);
            }

            var y = label.position.y + diffY * (diffHeight || 1);
            if (y > this.bounds.lowerRight().y) {
              y += this.bounds.height() - this.bounds.height() / (diffHeight || 1);
            }
            label.setX(x);
            label.setY(y);
            return;
          }

          switch (label.getEdgePosition()) {
            case 'starttop':
              var angle = this._getAngle(this.dockers[0], this.dockers[1]);
              var pos = this.dockers.first().bounds.center();

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left');
                label.verticalAlign('bottom');
                label.x = pos.x + label.getOffsetTop();
                label.y = pos.y - label.getOffsetTop();
                label.rotate(360 - angle, pos);
              } else {
                label.horizontalAlign('right');
                label.verticalAlign('bottom');
                label.x = pos.x - label.getOffsetTop();
                label.y = pos.y - label.getOffsetTop();
                label.rotate(180 - angle, pos);
              }

              break;

            case 'startmiddle':
              var angle = this._getAngle(this.dockers[0], this.dockers[1]);
              var pos = this.dockers.first().bounds.center();

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left');
                label.verticalAlign('bottom');
                label.x = pos.x + 2;
                label.y = pos.y + 4;
                label.rotate(360 - angle, pos);
              } else {
                label.horizontalAlign('right');
                label.verticalAlign('bottom');
                label.x = pos.x + 1;
                label.y = pos.y + 4;
                label.rotate(180 - angle, pos);
              }

              break;

            case 'startbottom':
              var angle = this._getAngle(this.dockers[0], this.dockers[1]);
              var pos = this.dockers.first().bounds.center();

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('left');
                label.verticalAlign('top');
                label.x = pos.x + label.getOffsetBottom();
                label.y = pos.y + label.getOffsetBottom();
                label.rotate(360 - angle, pos);
              } else {
                label.horizontalAlign('right');
                label.verticalAlign('top');
                label.x = pos.x - label.getOffsetBottom();
                label.y = pos.y + label.getOffsetBottom();
                label.rotate(180 - angle, pos);
              }

              break;
            case 'midtop':
              var numOfDockers = this.dockers.length;
              if (numOfDockers % 2 == 0) {
                var angle = this._getAngle(this.dockers[numOfDockers / 2 - 1], this.dockers[numOfDockers / 2]);
                var pos1 = this.dockers[numOfDockers / 2 - 1].bounds.center();
                var pos2 = this.dockers[numOfDockers / 2].bounds.center();
                var pos = { x: (pos1.x + pos2.x) / 2.0, y: (pos1.y + pos2.y) / 2.0 };

                label.horizontalAlign('center');
                label.verticalAlign('bottom');
                label.x = pos.x;
                label.y = pos.y - label.getOffsetTop();

                if (angle <= 90 || angle > 270) {
                  label.rotate(360 - angle, pos);
                } else {
                  label.rotate(180 - angle, pos);
                }
              } else {
                var index = parseInt(numOfDockers / 2);
                var angle = this._getAngle(this.dockers[index], this.dockers[index + 1]);
                var pos = this.dockers[index].bounds.center();

                if (angle <= 90 || angle > 270) {
                  label.horizontalAlign('left');
                  label.verticalAlign('bottom');
                  label.x = pos.x + label.getOffsetTop();
                  label.y = pos.y - label.getOffsetTop();
                  label.rotate(360 - angle, pos);
                } else {
                  label.horizontalAlign('right');
                  label.verticalAlign('bottom');
                  label.x = pos.x - label.getOffsetTop();
                  label.y = pos.y - label.getOffsetTop();
                  label.rotate(180 - angle, pos);
                }
              }

              break;
            case 'midbottom':
              var numOfDockers = this.dockers.length;
              if (numOfDockers % 2 == 0) {
                var angle = this._getAngle(this.dockers[numOfDockers / 2 - 1], this.dockers[numOfDockers / 2]);
                var pos1 = this.dockers[numOfDockers / 2 - 1].bounds.center();
                var pos2 = this.dockers[numOfDockers / 2].bounds.center();
                var pos = { x: (pos1.x + pos2.x) / 2.0, y: (pos1.y + pos2.y) / 2.0 };

                label.horizontalAlign('center');
                label.verticalAlign('top');
                label.x = pos.x;
                label.y = pos.y + label.getOffsetTop();

                if (angle <= 90 || angle > 270) {
                  label.rotate(360 - angle, pos);
                } else {
                  label.rotate(180 - angle, pos);
                }
              } else {
                var index = parseInt(numOfDockers / 2);
                var angle = this._getAngle(this.dockers[index], this.dockers[index + 1]);
                var pos = this.dockers[index].bounds.center();

                if (angle <= 90 || angle > 270) {
                  label.horizontalAlign('left');
                  label.verticalAlign('top');
                  label.x = pos.x + label.getOffsetBottom();
                  label.y = pos.y + label.getOffsetBottom();
                  label.rotate(360 - angle, pos);
                } else {
                  label.horizontalAlign('right');
                  label.verticalAlign('top');
                  label.x = pos.x - label.getOffsetBottom();
                  label.y = pos.y + label.getOffsetBottom();
                  label.rotate(180 - angle, pos);
                }
              }

              break;
            case 'endtop':
              var length = this.dockers.length;
              var angle = this._getAngle(this.dockers[length - 2], this.dockers[length - 1]);
              var pos = this.dockers.last().bounds.center();

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('right');
                label.verticalAlign('bottom');
                label.x = pos.x - label.getOffsetTop();
                label.y = pos.y - label.getOffsetTop();
                label.rotate(360 - angle, pos);
              } else {
                label.horizontalAlign('left');
                label.verticalAlign('bottom');
                label.x = pos.x + label.getOffsetTop();
                label.y = pos.y - label.getOffsetTop();
                label.rotate(180 - angle, pos);
              }

              break;
            case 'endbottom':
              var length = this.dockers.length;
              var angle = this._getAngle(this.dockers[length - 2], this.dockers[length - 1]);
              var pos = this.dockers.last().bounds.center();

              if (angle <= 90 || angle > 270) {
                label.horizontalAlign('right');
                label.verticalAlign('top');
                label.x = pos.x - label.getOffsetBottom();
                label.y = pos.y + label.getOffsetBottom();
                label.rotate(360 - angle, pos);
              } else {
                label.horizontalAlign('left');
                label.verticalAlign('top');
                label.x = pos.x + label.getOffsetBottom();
                label.y = pos.y + label.getOffsetBottom();
                label.rotate(180 - angle, pos);
              }

              break;
          }
        }.bind(this)
      );

      this.children.each(
        function (value) {
          if (value instanceof ORYX.Core.Node) {
            this.calculatePositionOfAttachedChildNode.call(this, value);
          }
        }.bind(this)
      );

      this.refreshAttachedNodes();
      this.refresh();

      this.isChanged = false;
      this._dockerUpdated = false;

      this._oldBounds = this.bounds.clone();
    }

    // IE10 specific fix, start and end-markes get left behind when moving path
    var userAgent = navigator.userAgent;
    if (navigator.appVersion.indexOf('MSIE 10') !== -1 || (userAgent.indexOf('Trident') !== -1 && userAgent.indexOf('rv:11') !== -1)) {
      this.node.parentNode.insertBefore(this.node, this.node);
    }
  },

  /**
   *  Moves a point to the upperLeft of a node's bounds.
   *
   *  @param {point} point
   *  	The point to move
   *  @param {ORYX.Core.Bounds} bounds
   *  	The Bounds of the related noe
   */
  movePointToUpperLeftOfNode: function (point, bounds) {
    point.x -= bounds.width() / 2;
    point.y -= bounds.height() / 2;
  },

  /**
   * Refreshes the visual representation of edge's attached nodes.
   */
  refreshAttachedNodes: function () {
    this.attachedNodePositionData.values().each(
      function (nodeData) {
        var startPoint = nodeData.segment.docker1.bounds.center();
        var endPoint = nodeData.segment.docker2.bounds.center();
        this.relativizePoint(startPoint);
        this.relativizePoint(endPoint);

        var newNodePosition = new Object();

        /* Calculate new x-coordinate */
        newNodePosition.x = startPoint.x + nodeData.relativDistanceFromDocker1 * (endPoint.x - startPoint.x);

        /* Calculate new y-coordinate */
        newNodePosition.y = startPoint.y + nodeData.relativDistanceFromDocker1 * (endPoint.y - startPoint.y);

        /* Convert new position to the upper left of the node */
        this.movePointToUpperLeftOfNode(newNodePosition, nodeData.node.bounds);

        /* Move node to its new position */
        nodeData.node.bounds.moveTo(newNodePosition);
        nodeData.node._update();
      }.bind(this)
    );
  },

  /**
   * Calculates the position of an edge's child node. The node is placed on
   * the path of the edge.
   *
   * @param {node}
   * 		The node to calculate the new position
   * @return {Point}
   * 		The calculated upper left point of the node's shape.
   */
  calculatePositionOfAttachedChildNode: function (node) {
    /* Initialize position */
    var position = new Object();
    position.x = 0;
    position.y = 0;

    /* Case: Node was just added */
    if (!this.attachedNodePositionData[node.getId()]) {
      this.attachedNodePositionData[node.getId()] = new Object();
      this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 0;
      this.attachedNodePositionData[node.getId()].node = node;
      this.attachedNodePositionData[node.getId()].segment = new Object();
      this.findEdgeSegmentForNode(node);
    } else if (node.isChanged) {
      this.findEdgeSegmentForNode(node);
    }
  },

  /**
   * Finds the appropriate edge segement for a node.
   * The segment is choosen, which has the smallest distance to the node.
   *
   * @param {ORYX.Core.Node} node
   * 		The concerning node
   */
  findEdgeSegmentForNode: function (node) {
    var length = this.dockers.length;
    var smallestDistance = undefined;

    for (i = 1; i < length; i++) {
      var lineP1 = this.dockers[i - 1].bounds.center();
      var lineP2 = this.dockers[i].bounds.center();
      this.relativizePoint(lineP1);
      this.relativizePoint(lineP2);

      var nodeCenterPoint = node.bounds.center();
      var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true);

      if ((distance || distance == 0) && ((!smallestDistance && smallestDistance != 0) || distance < smallestDistance)) {
        smallestDistance = distance;

        this.attachedNodePositionData[node.getId()].segment.docker1 = this.dockers[i - 1];
        this.attachedNodePositionData[node.getId()].segment.docker2 = this.dockers[i];
      }

      /* Either the distance does not match the segment or the distance
       * between docker1 and docker2 is 0
       *
       * In this case choose the nearest docker as attaching point.
       *
       */
      if (!distance && !smallestDistance && smallestDistance != 0) {
        ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP1) < ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP2)
          ? (this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 0)
          : (this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 1);
        this.attachedNodePositionData[node.getId()].segment.docker1 = this.dockers[i - 1];
        this.attachedNodePositionData[node.getId()].segment.docker2 = this.dockers[i];
      }
    }

    /* Calculate position on edge segment for the node */
    if (smallestDistance || smallestDistance == 0) {
      this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = this.getLineParameterForPosition(
        this.attachedNodePositionData[node.getId()].segment.docker1,
        this.attachedNodePositionData[node.getId()].segment.docker2,
        node
      );
    }
  },

  /**
   *
   * @param {ORYX.Core.Node|Object} node or position
   * @return {Object} An object with the following attribute: {ORYX.Core.Docker} fromDocker, {ORYX.Core.Docker} toDocker, {X/Y} position, {int} distance
   */
  findSegment: function (node) {
    var length = this.dockers.length;
    var result;

    var nodeCenterPoint = node instanceof ORYX.Core.UIObject ? node.bounds.center() : node;

    for (i = 1; i < length; i++) {
      var lineP1 = this.dockers[i - 1].bounds.center();
      var lineP2 = this.dockers[i].bounds.center();

      var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true);

      if (typeof distance == 'number' && (result === undefined || distance < result.distance)) {
        result = {
          distance: distance,
          fromDocker: this.dockers[i - 1],
          toDocker: this.dockers[i],
        };
      }
    }
    return result;
  },

  /**
   * Returns the value of the scalar to determine the position of the node on
   * line defined by docker1 and docker2.
   *
   * @param {point} docker1
   * 		The docker that defines the start of the line segment
   * @param {point} docker2
   * 		The docker that defines the end of the line segment
   * @param {ORYX.Core.Node} node
   * 		The concerning node
   *
   * @return {float} positionParameter
   * 		The scalar value to determine the position on the line
   */
  getLineParameterForPosition: function (docker1, docker2, node) {
    var dockerPoint1 = docker1.bounds.center();
    var dockerPoint2 = docker2.bounds.center();
    this.relativizePoint(dockerPoint1);
    this.relativizePoint(dockerPoint2);

    var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine(dockerPoint1, dockerPoint2, node.bounds.center(), true);
    if (!intersectionPoint) {
      return 0;
    }

    var relativeDistance =
      ORYX.Core.Math.getDistancePointToPoint(intersectionPoint, dockerPoint1) /
      ORYX.Core.Math.getDistancePointToPoint(dockerPoint1, dockerPoint2);

    return relativeDistance;
  },
  /**
   * Makes point relative to the upper left of the edge's bound.
   *
   * @param {point} point
   * 		The point to relativize
   */
  relativizePoint: function (point) {
    point.x -= this.bounds.upperLeft().x;
    point.y -= this.bounds.upperLeft().y;
  },

  /**
   * Move the first and last docker and calls the refresh method.
   * Attention: This does not calculates intersection point between the
   * edge and the bounded nodes. This only works if only the nodes are
   * moves.
   *
   */
  optimizedUpdate: function () {
    var updateDocker = function (docker) {
      if (!docker._dockedShape || !docker._dockedShapeBounds) return;
      var off = {
        x: docker._dockedShape.bounds.a.x - docker._dockedShapeBounds.a.x,
        y: docker._dockedShape.bounds.a.y - docker._dockedShapeBounds.a.y,
      };
      docker.bounds.moveBy(off);
      docker._dockedShapeBounds.moveBy(off);
    };

    updateDocker(this.dockers.first());
    updateDocker(this.dockers.last());

    this.refresh();
  },

  refresh: function () {
    //call base class refresh method
    arguments.callee.$.refresh.apply(this, arguments);

    //TODO consider points for marker mids
    var lastPoint;
    this._paths.each(
      function (path, index) {
        var dockers = this._dockersByPath[path.id];
        var c = undefined;
        var d = undefined;
        if (lastPoint) {
          d = 'M' + lastPoint.x + ' ' + lastPoint.y;
        } else {
          c = dockers[0].bounds.center();
          lastPoint = c;

          d = 'M' + c.x + ' ' + c.y;
        }

        for (var i = 1; i < dockers.length; i++) {
          // for each docker, draw a line to the center
          c = dockers[i].bounds.center();
          d = d + 'L' + c.x + ' ' + c.y + ' ';
          lastPoint = c;
        }

        path.setAttributeNS(null, 'd', d);
        this._interactionPaths[index].setAttributeNS(null, 'd', d);
      }.bind(this)
    );

    /* move child shapes of an edge */
    if (this.getChildNodes().length > 0) {
      var x = this.bounds.upperLeft().x;
      var y = this.bounds.upperLeft().y;

      this.node.firstChild.childNodes[1].setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')');
    }
  },

  /**
   * Calculate the Border Intersection Point between two points
   * @param {PointA}
   * @param {PointB}
   */
  getIntersectionPoint: function () {
    var length = Math.floor(this.dockers.length / 2);

    return ORYX.Core.Math.midPoint(this.dockers[length - 1].bounds.center(), this.dockers[length].bounds.center());
  },

  /**
   * Returns TRUE if the bounds is over the edge
   * @param {Bounds}
   *
   */
  isBoundsIncluded: function (bounds) {
    var dockers = this.dockers,
      size = dockers.length;
    return dockers.any(function (docker, i) {
      if (i == size - 1) {
        return false;
      }
      var a = docker.bounds.center();
      var b = dockers[i + 1].bounds.center();

      return ORYX.Core.Math.isRectOverLine(a.x, a.y, b.x, b.y, bounds.a.x, bounds.a.y, bounds.b.x, bounds.b.y);
    });
  },

  /**
   * Calculate if the point is inside the Shape
   * @param {PointX}
   * @param {PointY}
   */
  isPointIncluded: function (pointX, pointY) {
    var isbetweenAB = this.absoluteBounds().isIncluded(pointX, pointY, ORYX.CONFIG.OFFSET_EDGE_BOUNDS);

    var isPointIncluded = undefined;

    if (isbetweenAB && this.dockers.length > 0) {
      var i = 0;
      var point1, point2;

      do {
        point1 = this.dockers[i].bounds.center();
        point2 = this.dockers[++i].bounds.center();

        isPointIncluded = ORYX.Core.Math.isPointInLine(
          pointX,
          pointY,
          point1.x,
          point1.y,
          point2.x,
          point2.y,
          ORYX.CONFIG.OFFSET_EDGE_BOUNDS
        );
      } while (!isPointIncluded && i < this.dockers.length - 1);
    }

    return isPointIncluded;
  },

  /**
   * Calculate if the point is over an special offset area
   * @param {Point}
   */
  isPointOverOffset: function () {
    return false;
  },

  /**
   * Returns TRUE if the given node
   * is a child node of the shapes node
   * @param {Element} node
   * @return {Boolean}
   *
   */
  containsNode: function (node) {
    if (this._paths.include(node) || this._interactionPaths.include(node)) {
      return true;
    }
    return false;
  },

  /**
   * Returns the angle of the line between two dockers
   * (0 - 359.99999999)
   */
  _getAngle: function (docker1, docker2) {
    var p1 = docker1 instanceof ORYX.Core.Controls.Docker ? docker1.absoluteCenterXY() : docker1;
    var p2 = docker2 instanceof ORYX.Core.Controls.Docker ? docker2.absoluteCenterXY() : docker2;

    return ORYX.Core.Math.getAngle(p1, p2);
  },

  alignDockers: function () {
    this._update(true);

    var firstPoint = this.dockers.first().bounds.center();
    var lastPoint = this.dockers.last().bounds.center();

    var deltaX = lastPoint.x - firstPoint.x;
    var deltaY = lastPoint.y - firstPoint.y;

    var numOfDockers = this.dockers.length - 1;

    this.dockers.each(
      function (docker, index) {
        var part = index / numOfDockers;
        docker.bounds.unregisterCallback(this._dockerChangedCallback);
        docker.bounds.moveTo(firstPoint.x + part * deltaX, firstPoint.y + part * deltaY);
        docker.bounds.registerCallback(this._dockerChangedCallback);
      }.bind(this)
    );

    this._dockerChanged();
  },

  add: function (shape) {
    arguments.callee.$.add.apply(this, arguments);

    // If the new shape is a Docker which is not contained
    if (shape instanceof ORYX.Core.Controls.Docker && this.dockers.include(shape)) {
      // Add it to the dockers list ordered by paths
      var pathArray = this._dockersByPath.values()[0];
      if (pathArray) {
        pathArray.splice(this.dockers.indexOf(shape), 0, shape);
      }

      /* Perform nessary adjustments on the edge's child shapes */
      this.handleChildShapesAfterAddDocker(shape);
    }
  },

  /**
   * Performs nessary adjustments on the edge's child shapes.
   *
   * @param {ORYX.Core.Controls.Docker} docker
   * 		The added docker
   */
  handleChildShapesAfterAddDocker: function (docker) {
    /* Ensure type of Docker */
    if (!docker instanceof ORYX.Core.Controls.Docker) {
      return undefined;
    }

    var index = this.dockers.indexOf(docker);
    if (!(0 < index && index < this.dockers.length - 1)) {
      /* Exception: Expect added docker between first and last node of the edge */
      return undefined;
    }

    /* Get child nodes concerning the segment of the new docker */
    var startDocker = this.dockers[index - 1];
    var endDocker = this.dockers[index + 1];

    /* Adjust the position of edge's child nodes */
    var segmentElements = this.getAttachedNodePositionDataForSegment(startDocker, endDocker);

    var lengthSegmentPart1 = ORYX.Core.Math.getDistancePointToPoint(startDocker.bounds.center(), docker.bounds.center());
    var lengthSegmentPart2 = ORYX.Core.Math.getDistancePointToPoint(endDocker.bounds.center(), docker.bounds.center());

    if (!(lengthSegmentPart1 + lengthSegmentPart2)) {
      return;
    }

    var relativDockerPosition = lengthSegmentPart1 / (lengthSegmentPart1 + lengthSegmentPart2);

    segmentElements.each(function (nodePositionData) {
      /* Assign child node to the new segment */
      if (nodePositionData.value.relativDistanceFromDocker1 < relativDockerPosition) {
        /* Case: before added Docker */
        nodePositionData.value.segment.docker2 = docker;
        nodePositionData.value.relativDistanceFromDocker1 = nodePositionData.value.relativDistanceFromDocker1 / relativDockerPosition;
      } else {
        /* Case: after added Docker */
        nodePositionData.value.segment.docker1 = docker;
        var newFullDistance = 1 - relativDockerPosition;
        var relativPartOfSegment = nodePositionData.value.relativDistanceFromDocker1 - relativDockerPosition;

        nodePositionData.value.relativDistanceFromDocker1 = relativPartOfSegment / newFullDistance;
      }
    });

    // Update all labels reference points
    this.getLabels().each(
      function (label) {
        var ref = label.getReferencePoint();
        if (!ref) {
          return;
        }
        var index = this.dockers.indexOf(docker);
        if (index >= ref.segment.fromIndex && index <= ref.segment.toIndex) {
          var segment = this.findSegment(ref.intersection);
          if (!segment) {
            // Choose whether the first of the last segment
            segment.fromDocker = ref.segment.fromIndex >= this.dockers.length / 2 ? this.dockers[0] : this.dockers[this.dockers.length - 2];
            segment.toDocker = this.dockers[this.dockers.indexOf(from) + 1]; // The next one if the to docker
          }

          var fromPosition = segment.fromDocker.bounds.center(),
            toPosition = segment.toDocker.bounds.center();

          var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(
            fromPosition, // P1 - Center of the first docker
            toPosition, // P2 - Center of the second docker
            ref.intersection, // P3 - Center of the label
            true
          );
          //var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(ref.segment.fromPosition, ref.segment.toPosition, ref.intersection);
          //intersection = ORYX.Core.Math.getPointBetweenTwoPoints(fromPosition, toPosition, isNaN(oldDistance) ? 0.5 : (lengthOld*oldDistance)/lengthNew);

          // Update the reference point
          this.updateReferencePointOfLabel(label, intersection, segment.fromDocker, segment.toDocker, true);
        }
      }.bind(this)
    );

    /* Update attached nodes visual representation */
    this.refreshAttachedNodes();
  },

  /**
   *	Returns elements from {@link attachedNodePositiondata} that match the
   *  segement defined by startDocker and endDocker.
   *
   *  @param {ORYX.Core.Controls.Docker} startDocker
   *  	The docker defining the begin of the segment.
   *  @param {ORYX.Core.Controls.Docker} endDocker
   *  	The docker defining the begin of the segment.
   *
   *  @return {Hash} attachedNodePositionData
   *  	Child elements matching the segment
   */
  getAttachedNodePositionDataForSegment: function (startDocker, endDocker) {
    /* Ensure that the segment is defined correctly */
    if (!(startDocker instanceof ORYX.Core.Controls.Docker && endDocker instanceof ORYX.Core.Controls.Docker)) {
      return [];
    }

    /* Get elements of the segment */
    var elementsOfSegment = this.attachedNodePositionData.findAll(function (nodePositionData) {
      return nodePositionData.value.segment.docker1 === startDocker && nodePositionData.value.segment.docker2 === endDocker;
    });

    /* Return a Hash in each case */
    if (!elementsOfSegment) {
      return [];
    }

    return elementsOfSegment;
  },

  /**
   * Removes an edge's child shape
   */
  remove: function (shape) {
    arguments.callee.$.remove.apply(this, arguments);

    if (this.attachedNodePositionData[shape.getId()]) {
      delete this.attachedNodePositionData[shape.getId()];
    }

    /* Adjust child shapes if neccessary */
    if (shape instanceof ORYX.Core.Controls.Docker) {
      this.handleChildShapesAfterRemoveDocker(shape);
    }
  },

  updateReferencePointOfLabel: function (label, intersection, from, to, dirty) {
    if (!label.getReferencePoint() || !label.isVisible) {
      return;
    }

    var ref = label.getReferencePoint();

    //
    if (ref.orientation && ref.orientation !== 'ce') {
      var angle = this._getAngle(from, to);
      if (ref.distance >= 0) {
        if (angle == 0) {
          label.horizontalAlign('left'); //ref.orientation == "lr" ? "right" : "left");
          label.verticalAlign('bottom');
        } else if (angle > 0 && angle < 90) {
          label.horizontalAlign('right');
          label.verticalAlign('bottom');
        } else if (angle == 90) {
          label.horizontalAlign('right');
          label.verticalAlign('top'); //ref.orientation == "lr" ? "bottom" : "top");
        } else if (angle > 90 && angle < 180) {
          label.horizontalAlign('right');
          label.verticalAlign('top');
        } else if (angle == 180) {
          label.horizontalAlign('left'); //ref.orientation == "ur" ? "right" : "left");
          label.verticalAlign('top');
        } else if (angle > 180 && angle < 270) {
          label.horizontalAlign('left');
          label.verticalAlign('top');
        } else if (angle == 270) {
          label.horizontalAlign('left');
          label.verticalAlign('top'); //ref.orientation == "ll" ? "bottom" : "top");
        } else if (angle > 270 && angle <= 360) {
          label.horizontalAlign('left');
          label.verticalAlign('bottom');
        }
      } else {
        if (angle == 0) {
          label.horizontalAlign('left'); //ref.orientation == "ur" ? "right" : "left");
          label.verticalAlign('top');
        } else if (angle > 0 && angle < 90) {
          label.horizontalAlign('left');
          label.verticalAlign('top');
        } else if (angle == 90) {
          label.horizontalAlign('left');
          label.verticalAlign('top'); //ref.orientation == "ll" ? "bottom" : "top");
        } else if (angle > 90 && angle < 180) {
          label.horizontalAlign('left');
          label.verticalAlign('bottom');
        } else if (angle == 180) {
          label.horizontalAlign('left'); //ref.orientation == "lr" ? "right" : "left");
          label.verticalAlign('bottom');
        } else if (angle > 180 && angle < 270) {
          label.horizontalAlign('right');
          label.verticalAlign('bottom');
        } else if (angle == 270) {
          label.horizontalAlign('right');
          label.verticalAlign('top'); //ref.orientation == "lr" ? "bottom" : "top");
        } else if (angle > 270 && angle <= 360) {
          label.horizontalAlign('right');
          label.verticalAlign('top');
        }
      }
      ref.iorientation = ref.iorientation || ref.orientation;
      ref.orientation = (label.verticalAlign() == 'top' ? 'u' : 'l') + (label.horizontalAlign() == 'left' ? 'l' : 'r');
    }

    label.setReferencePoint(
      jQuery.extend(
        {},
        {
          intersection: intersection,
          segment: {
            from: from,
            fromIndex: this.dockers.indexOf(from),
            fromPosition: from.bounds.center(),
            to: to,
            toIndex: this.dockers.indexOf(to),
            toPosition: to.bounds.center(),
          },
          dirty: dirty || false,
        },
        ref
      )
    );
  },
  /**
   * 	Adjusts the child shapes of an edges after a docker was removed.
   *
   *  @param{ORYX.Core.Controls.Docker} docker
   *  	The removed docker.
   */
  handleChildShapesAfterRemoveDocker: function (docker) {
    /* Ensure docker type */
    if (!(docker instanceof ORYX.Core.Controls.Docker)) {
      return;
    }

    this.attachedNodePositionData.each(
      function (nodePositionData) {
        if (nodePositionData.value.segment.docker1 === docker) {
          /* The new start of the segment is the predecessor of docker2. */
          var index = this.dockers.indexOf(nodePositionData.value.segment.docker2);
          if (index == -1) {
            return;
          }
          nodePositionData.value.segment.docker1 = this.dockers[index - 1];
        } else if (nodePositionData.value.segment.docker2 === docker) {
          /* The new end of the segment is the successor of docker1. */
          var index = this.dockers.indexOf(nodePositionData.value.segment.docker1);
          if (index == -1) {
            return;
          }
          nodePositionData.value.segment.docker2 = this.dockers[index + 1];
        }
      }.bind(this)
    );

    // Update all labels reference points
    this.getLabels().each(
      function (label) {
        var ref = label.getReferencePoint();
        if (!ref) {
          return;
        }
        var from = ref.segment.from;
        var to = ref.segment.to;

        if (from !== docker && to !== docker) {
          return;
        }

        var segment = this.findSegment(ref.intersection);
        if (!segment) {
          from = segment.fromDocker;
          to = segment.toDocker;
        } else {
          from = from === docker ? this.dockers[this.dockers.indexOf(to) - 1] : from;
          to = this.dockers[this.dockers.indexOf(from) + 1];
        }

        var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(from.bounds.center(), to.bounds.center(), ref.intersection, true);
        // Update the reference point
        this.updateReferencePointOfLabel(label, intersection, from, to, true);
      }.bind(this)
    );

    /* Update attached nodes visual representation */
    this.refreshAttachedNodes();
  },

  /**
   *@deprecated Use the .createDocker() Method and set the point via the bounds
   */
  addDocker: function (position, exDocker) {
    var lastDocker;
    var result;
    this._dockersByPath.any(
      function (pair) {
        return pair.value.any(
          function (docker, index) {
            if (!lastDocker) {
              lastDocker = docker;
              return false;
            } else {
              var point1 = lastDocker.bounds.center();
              var point2 = docker.bounds.center();

              var additionalIEZoom = 1;
              if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                var ua = navigator.userAgent;
                if (ua.indexOf('MSIE') >= 0) {
                  //IE 10 and below
                  var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                  if (zoom !== 100) {
                    additionalIEZoom = zoom / 100;
                  }
                }
              }

              if (additionalIEZoom !== 1) {
                position.x = position.x / additionalIEZoom;
                position.y = position.y / additionalIEZoom;
              }

              if (ORYX.Core.Math.isPointInLine(position.x, position.y, point1.x, point1.y, point2.x, point2.y, 10)) {
                var path = this._paths.find(function (path) {
                  return path.id === pair.key;
                });
                if (path) {
                  var allowAttr = path.getAttributeNS(NAMESPACE_ORYX, 'allowDockers');
                  if (allowAttr && allowAttr.toLowerCase() === 'no') {
                    return true;
                  }
                }

                var newDocker = exDocker ? exDocker : this.createDocker(this.dockers.indexOf(lastDocker) + 1, position);
                newDocker.bounds.centerMoveTo(position);
                if (exDocker) this.add(newDocker, this.dockers.indexOf(lastDocker) + 1);
                result = newDocker;
                return true;
              } else {
                lastDocker = docker;
                return false;
              }
            }
          }.bind(this)
        );
      }.bind(this)
    );
    return result;
  },

  removeDocker: function (docker) {
    if (this.dockers.length > 2 && !(this.dockers.first() === docker)) {
      this._dockersByPath.any(
        function (pair) {
          if (pair.value.member(docker)) {
            if (docker === pair.value.last()) {
              return true;
            } else {
              this.remove(docker);
              this._dockersByPath[pair.key] = pair.value.without(docker);
              this.isChanged = true;
              this._dockerChanged();
              return true;
            }
          }
          return false;
        }.bind(this)
      );
    }
  },

  /**
   * Removes all dockers from the edge which are on
   * the line between two dockers
   * @return {Object} Removed dockers in an indicied array
   * (key is the removed position of the docker, value is docker themselve)
   */
  removeUnusedDockers: function () {
    var marked = $H({});

    this.dockers.each(
      function (docker, i) {
        if (i == 0 || i == this.dockers.length - 1) {
          return;
        }
        var previous = this.dockers[i - 1];

        /* Do not consider already removed dockers */
        if (marked.values().indexOf(previous) != -1 && this.dockers[i - 2]) {
          previous = this.dockers[i - 2];
        }
        var next = this.dockers[i + 1];

        var cp = previous.getDockedShape() && previous.referencePoint ? previous.getAbsoluteReferencePoint() : previous.bounds.center();
        var cn = next.getDockedShape() && next.referencePoint ? next.getAbsoluteReferencePoint() : next.bounds.center();
        var cd = docker.bounds.center();

        if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 1)) {
          marked[i] = docker;
        }
      }.bind(this)
    );

    marked.each(
      function (docker) {
        this.removeDocker(docker.value);
      }.bind(this)
    );

    if (marked.values().length > 0) {
      this._update(true);
    }

    return marked;
  },

  /**
   * Initializes the Edge after loading the SVG representation of the edge.
   * @param {SVGDocument} svgDocument
   */
  _init: function (svgDocument) {
    arguments.callee.$._init.apply(this, arguments);

    var minPointX, minPointY, maxPointX, maxPointY;

    //init markers
    var defs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, 'defs');
    if (defs.length > 0) {
      defs = defs[0];
      var markerElements = $A(defs.getElementsByTagNameNS(NAMESPACE_SVG, 'marker'));
      var marker;
      var me = this;
      markerElements.each(function (markerElement) {
        try {
          marker = new ORYX.Core.SVG.SVGMarker(markerElement.cloneNode(true));
          me._markers[marker.id] = marker;
          var textElements = $A(marker.element.getElementsByTagNameNS(NAMESPACE_SVG, 'text'));
          var label;
          textElements.each(function (textElement) {
            label = new ORYX.Core.SVG.Label({
              textElement: textElement,
              shapeId: this.id,
            });
            me._labels[label.id] = label;
          });
        } catch (e) {}
      });
    }

    var gs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, 'g');
    if (gs.length <= 0) {
      throw 'Edge: No g element found.';
    }
    var g = gs[0];

    g.setAttributeNS(null, 'id', null);

    var isFirst = true;

    $A(g.childNodes).each(
      function (path, index) {
        if (ORYX.Editor.checkClassType(path, SVGPathElement)) {
          path = path.cloneNode(false);

          var pathId = this.id + '_' + index;
          path.setAttributeNS(null, 'id', pathId);
          this._paths.push(path);

          //check, if markers are set and update the id
          var markersByThisPath = [];
          var markerUrl = path.getAttributeNS(null, 'marker-start');

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip();
            markerUrl = markerUrl.replace(/^url\(#/, '');

            var markerStartId = this.getValidMarkerId(markerUrl);
            path.setAttributeNS(null, 'marker-start', 'url(#' + markerStartId + ')');

            markersByThisPath.push(this._markers[markerStartId]);
          }

          markerUrl = path.getAttributeNS(null, 'marker-mid');

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip();
            markerUrl = markerUrl.replace(/^url\(#/, '');
            var markerMidId = this.getValidMarkerId(markerUrl);
            path.setAttributeNS(null, 'marker-mid', 'url(#' + markerMidId + ')');

            markersByThisPath.push(this._markers[markerMidId]);
          }

          markerUrl = path.getAttributeNS(null, 'marker-end');

          if (markerUrl && markerUrl !== '') {
            markerUrl = markerUrl.strip();

            var markerEndId = this.getValidMarkerId(markerUrl);
            path.setAttributeNS(null, 'marker-end', 'url(#' + markerEndId + ')');

            markersByThisPath.push(this._markers[markerEndId]);
          }

          this._markersByPath[pathId] = markersByThisPath;

          //init dockers
          var parser = new PathParser();
          var handler = new ORYX.Core.SVG.PointsPathHandler();
          parser.setHandler(handler);
          parser.parsePath(path);

          if (handler.points.length < 4) {
            throw 'Edge: Path has to have two or more points specified.';
          }

          this._dockersByPath[pathId] = [];

          for (var i = 0; i < handler.points.length; i += 2) {
            //handler.points.each((function(point, pIndex){
            var x = handler.points[i];
            var y = handler.points[i + 1];
            if (isFirst || i > 0) {
              var docker = new ORYX.Core.Controls.Docker({
                eventHandlerCallback: this.eventHandlerCallback,
              });
              docker.bounds.centerMoveTo(x, y);
              docker.bounds.registerCallback(this._dockerChangedCallback);
              this.add(docker, this.dockers.length);

              //this._dockersByPath[pathId].push(docker);

              //calculate minPoint and maxPoint
              if (minPointX) {
                minPointX = Math.min(x, minPointX);
                minPointY = Math.min(y, minPointY);
              } else {
                minPointX = x;
                minPointY = y;
              }

              if (maxPointX) {
                maxPointX = Math.max(x, maxPointX);
                maxPointY = Math.max(y, maxPointY);
              } else {
                maxPointX = x;
                maxPointY = y;
              }
            }
            //}).bind(this));
          }
          isFirst = false;
        }
      }.bind(this)
    );

    this.bounds.set(minPointX, minPointY, maxPointX, maxPointY);

    if (false && (this.bounds.width() === 0 || this.bounds.height() === 0)) {
      var width = this.bounds.width();
      var height = this.bounds.height();

      this.bounds.extend({
        x: width === 0 ? 2 : 0,
        y: height === 0 ? 2 : 0,
      });

      this.bounds.moveBy({
        x: width === 0 ? -1 : 0,
        y: height === 0 ? -1 : 0,
      });
    }

    this._oldBounds = this.bounds.clone();

    //add paths to this.node
    this._paths.reverse();
    var paths = [];
    this._paths.each(
      function (path) {
        paths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(path));
      }.bind(this)
    );

    this._paths = paths;

    //init interaction path
    this._paths.each(
      function (path) {
        var iPath = path.cloneNode(false);
        iPath.setAttributeNS(null, 'id', undefined);
        iPath.setAttributeNS(null, 'stroke-width', 10);
        iPath.setAttributeNS(null, 'visibility', 'hidden');
        iPath.setAttributeNS(null, 'stroke-dasharray', null);
        iPath.setAttributeNS(null, 'stroke', 'black');
        iPath.setAttributeNS(null, 'fill', 'none');
        iPath.setAttributeNS(null, 'title', this.getStencil().title());
        this._interactionPaths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(iPath));
      }.bind(this)
    );

    this._paths.reverse();
    this._interactionPaths.reverse();

    /**initialize labels*/
    var textElems = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text');

    $A(textElems).each(
      function (textElem) {
        var label = new ORYX.Core.SVG.Label({
          textElement: textElem,
          shapeId: this.id,
        });
        this.node.childNodes[0].childNodes[0].appendChild(label.node);
        this._labels[label.id] = label;

        label.registerOnChange(this.layout.bind(this));
      }.bind(this)
    );

    this.propertiesChanged.each(function (pair) {
      pair.value = true;
    });

    //if(this.dockers.length == 2) {

    //  }

    //this._update(true);
  },

  getValidMarkerId: function (markerUrl) {
    if (markerUrl.indexOf('url("#') >= 0) {
      // Fix for IE9, additional quotes are added to the <id
      var rawId = markerUrl.replace(/^url\(\"#/, '').replace(/\"\)$/, '');
      return this.id + rawId;
    } else {
      markerUrl = markerUrl.replace(/^url\(#/, '');
      return this.id.concat(markerUrl.replace(/\)$/, ''));
    }
  },

  /**
   * Adds all necessary markers of this Edge to the SVG document.
   * Has to be called, while this.node is part of DOM.
   */
  addMarkers: function (defs) {
    this._markers.each(function (marker) {
      if (!defs.ownerDocument.getElementById(marker.value.id)) {
        marker.value.element = defs.appendChild(marker.value.element);
      }
    });
  },

  /**
   * Removes all necessary markers of this Edge from the SVG document.
   * Has to be called, while this.node is part of DOM.
   */
  removeMarkers: function () {
    var svgElement = this.node.ownerSVGElement;
    if (svgElement) {
      var defs = svgElement.getElementsByTagNameNS(NAMESPACE_SVG, 'defs');
      if (defs.length > 0) {
        defs = defs[0];
        this._markers.each(function (marker) {
          var foundMarker = defs.ownerDocument.getElementById(marker.value.id);
          if (foundMarker) {
            marker.value.element = defs.removeChild(marker.value.element);
          }
        });
      }
    }
  },

  /**
   * Calls when a docker has changed
   */
  _dockerChanged: function () {
    //this._update(true);
    this._dockerUpdated = true;
  },

  serialize: function () {
    var result = arguments.callee.$.serialize.apply(this);

    //add dockers triple
    var value = '';
    this._dockersByPath.each(
      function (pair) {
        pair.value.each(function (docker) {
          var position = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center();
          value = value.concat(position.x + ' ' + position.y + ' ');
        });

        value += ' # ';
      }.bind(this)
    );
    result.push({
      name: 'dockers',
      prefix: 'oryx',
      value: value,
      type: 'literal',
    });

    //add parent triple dependant on the dockedShapes
    //TODO change this when canvas becomes a resource
    /*        var source = this.dockers.first().getDockedShape();
        var target = this.dockers.last().getDockedShape();
        var sharedParent;
        if (source && target) {
            //get shared parent
            while (source.parent) {
                source = source.parent;
                if (source instanceof ORYX.Core.Canvas) {
                    sharedParent = source;
                    break;
                }
                else {
                    var targetParent = target.parent;
                    var found;
                    while (targetParent) {
                        if (source === targetParent) {
                            sharedParent = source;
                            found = true;
                            break;
                        }
                        else {
                            targetParent = targetParent.parent;
                        }
                    }
                    if (found) {
                        break;
                    }
                }
            }
        }
        else 
            if (source) {
                sharedParent = source.parent;
            }
            else 
                if (target) {
                    sharedParent = target.parent;
                }
*/
    //if (sharedParent) {
    /*            result.push({
                name: 'parent',
                prefix: 'raziel',
                //value: '#' + ERDF.__stripHashes(sharedParent.resourceId),
                value: '#' + ERDF.__stripHashes(this.getCanvas().resourceId),
                type: 'resource'
            });*/
    //}

    //serialize target and source
    var lastDocker = this.dockers.last();

    var target = lastDocker.getDockedShape();

    if (target) {
      result.push({
        name: 'target',
        prefix: 'raziel',
        value: '#' + ERDF.__stripHashes(target.resourceId),
        type: 'resource',
      });
    }

    try {
      //result = this.getStencil().serialize(this, result);
      var serializeEvent = this.getStencil().serialize();

      /*
       * call serialize callback by reference, result should be found
       * in serializeEvent.result
       */
      if (serializeEvent.type) {
        serializeEvent.shape = this;
        serializeEvent.data = result;
        serializeEvent.result = undefined;
        serializeEvent.forceExecution = true;

        this._delegateEvent(serializeEvent);

        if (serializeEvent.result) {
          result = serializeEvent.result;
        }
      }
    } catch (e) {}
    return result;
  },

  deserialize: function (data) {
    try {
      //data = this.getStencil().deserialize(this, data);

      var deserializeEvent = this.getStencil().deserialize();

      /*
       * call serialize callback by reference, result should be found
       * in serializeEventInfo.result
       */
      if (deserializeEvent.type) {
        deserializeEvent.shape = this;
        deserializeEvent.data = data;
        deserializeEvent.result = undefined;
        deserializeEvent.forceExecution = true;

        this._delegateEvent(deserializeEvent);
        if (deserializeEvent.result) {
          data = deserializeEvent.result;
        }
      }
    } catch (e) {}

    // Set the outgoing shapes
    var target = data.find(function (ser) {
      return ser.prefix + '-' + ser.name == 'raziel-target';
    });
    var targetShape;
    if (target) {
      targetShape = this.getCanvas().getChildShapeByResourceId(target.value);
    }

    var outgoing = data.findAll(function (ser) {
      return ser.prefix + '-' + ser.name == 'raziel-outgoing';
    });
    outgoing.each(
      function (obj) {
        // TODO: Look at Canvas
        if (!this.parent) {
          return;
        }

        // Set outgoing Shape
        var next = this.getCanvas().getChildShapeByResourceId(obj.value);

        if (next) {
          if (next == targetShape) {
            // If this is an edge, set the last docker to the next shape
            this.dockers.last().setDockedShape(next);
            this.dockers.last().setReferencePoint({ x: next.bounds.width() / 2.0, y: next.bounds.height() / 2.0 });
          } else if (next instanceof ORYX.Core.Edge) {
            //Set the first docker of the next shape
            next.dockers.first().setDockedShape(this);
            //next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
          } /*else if(next.dockers.length > 0) { //next is a node and next has a docker
					next.dockers.first().setDockedShape(this);
					next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
				}*/
        }
      }.bind(this)
    );

    var oryxDockers = data.find(function (obj) {
      return obj.prefix === 'oryx' && obj.name === 'dockers';
    });

    if (oryxDockers) {
      var dataByPath = oryxDockers.value.split('#').without('').without(' ');

      dataByPath.each(
        function (data, index) {
          var values = data.replace(/,/g, ' ').split(' ').without('');

          //for each docker two values must be defined
          if (values.length % 2 === 0) {
            var path = this._paths[index];

            if (path) {
              if (index === 0) {
                while (this._dockersByPath[path.id].length > 2) {
                  this.removeDocker(this._dockersByPath[path.id][1]);
                }
              } else {
                while (this._dockersByPath[path.id].length > 1) {
                  this.removeDocker(this._dockersByPath[path.id][0]);
                }
              }

              var dockersByPath = this._dockersByPath[path.id];

              if (index === 0) {
                //set position of first docker
                var x = parseFloat(values.shift());
                var y = parseFloat(values.shift());

                if (dockersByPath.first().getDockedShape()) {
                  dockersByPath.first().setReferencePoint({
                    x: x,
                    y: y,
                  });
                } else {
                  dockersByPath.first().bounds.centerMoveTo(x, y);
                }
              }

              //set position of last docker
              y = parseFloat(values.pop());
              x = parseFloat(values.pop());

              if (dockersByPath.last().getDockedShape()) {
                dockersByPath.last().setReferencePoint({
                  x: x,
                  y: y,
                });
              } else {
                dockersByPath.last().bounds.centerMoveTo(x, y);
              }

              //add additional dockers
              for (var i = 0; i < values.length; i++) {
                x = parseFloat(values[i]);
                y = parseFloat(values[++i]);

                var newDocker = this.createDocker();
                newDocker.bounds.centerMoveTo(x, y);

                //this.dockers = this.dockers.without(newDocker);
                //this.dockers.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
                //dockersByPath.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
              }
            }
          }
        }.bind(this)
      );
    } else {
      this.alignDockers();
    }

    arguments.callee.$.deserialize.apply(this, arguments);

    this._changed();
  },

  toString: function () {
    return this.getStencil().title() + ' ' + this.id;
  },

  /**
   * @return {ORYX.Core.Shape} Returns last docked shape or null.
   */
  getTarget: function () {
    return this.dockers.last() ? this.dockers.last().getDockedShape() : null;
  },

  /**
   * @return {ORYX.Core.Shape} Returns the first docked shape or null
   */
  getSource: function () {
    return this.dockers.first() ? this.dockers.first().getDockedShape() : null;
  },

  /**
   * Checks whether the edge is at least docked to one shape.
   *
   * @return {boolean} True if edge is docked
   */
  isDocked: function () {
    var isDocked = false;
    this.dockers.each(function (docker) {
      if (docker.isDocked()) {
        isDocked = true;
        throw $break;
      }
    });
    return isDocked;
  },

  /**
   * Calls {@link ORYX.Core.AbstractShape#toJSON} and add a some stencil set information.
   */
  toJSON: function () {
    var json = arguments.callee.$.toJSON.apply(this, arguments);

    if (this.getTarget()) {
      json.target = {
        resourceId: this.getTarget().resourceId,
      };
    }

    return json;
  },
};
ORYX.Core.Edge = ORYX.Core.Shape.extend(ORYX.Core.Edge);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Plugins) {
  ORYX.Plugins = {};
}

/**
   This abstract plugin class can be used to build plugins on.
   It provides some more basic functionality like registering events (on*-handlers)...
   @example
    ORYX.Plugins.MyPlugin = ORYX.Plugins.AbstractPlugin.extend({
        construct: function() {
            // Call super class constructor
            arguments.callee.$.construct.apply(this, arguments);
            
            [...]
        },
        [...]
    });
   
   @class ORYX.Plugins.AbstractPlugin
   @constructor Creates a new instance
   @author Willi Tscheschner
*/
ORYX.Plugins.AbstractPlugin = Clazz.extend({
  /**
   * The facade which offer editor-specific functionality
   * @type Facade
   * @memberOf ORYX.Plugins.AbstractPlugin.prototype
   */
  facade: null,

  construct: function (facade) {
    this.facade = facade;

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.onLoaded.bind(this));
  },

  /**
       Overwrite to handle load event. TODO: Document params!!!
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  onLoaded: function () {},

  /**
       Overwrite to handle selection changed event. TODO: Document params!!!
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  onSelectionChanged: function () {},

  /**
       Show overlay on given shape.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
       @example
       showOverlay(
           myShape,
           { stroke: "green" },
           ORYX.Editor.graft("http://www.w3.org/2000/svg", null, ['path', {
               "title": "Click the element to execute it!",
               "stroke-width": 2.0,
               "stroke": "black",
               "d": "M0,-5 L5,0 L0,5 Z",
               "line-captions": "round"
           }])
       )
       @param {Oryx.XXX.Shape[]} shapes One shape or array of shapes the overlay should be put on
       @param {Oryx.XXX.Attributes} attributes some attributes...
       @param {Oryx.svg.node} svgNode The svg node which should be used as overlay
       @param {String} [svgNode="NW"] The svg node position where the overlay should be placed
    */
  showOverlay: function (shapes, attributes, svgNode, svgNodePosition) {
    if (!(shapes instanceof Array)) {
      shapes = [shapes];
    }

    // Define Shapes
    shapes = shapes
      .map(
        function (shape) {
          var el = shape;
          if (typeof shape == 'string') {
            el = this.facade.getCanvas().getChildShapeByResourceId(shape);
            el = el || this.facade.getCanvas().getChildById(shape, true);
          }
          return el;
        }.bind(this)
      )
      .compact();

    // Define unified id
    if (!this.overlayID) {
      this.overlayID = this.type + ORYX.Editor.provideId();
    }

    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_SHOW,
      id: this.overlayID,
      shapes: shapes,
      attributes: attributes,
      node: svgNode,
      nodePosition: svgNodePosition || 'NW',
    });
  },

  /**
       Hide current overlay.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
    */
  hideOverlay: function () {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_HIDE,
      id: this.overlayID,
    });
  },

  /**
       Does a transformation with the given xslt stylesheet.
       @methodOf ORYX.Plugins.AbstractPlugin.prototype
       @param {String} data The data (e.g. eRDF) which should be transformed
       @param {String} stylesheet URL of a stylesheet which should be used for transforming data.
    */
  doTransform: function (data, stylesheet) {
    if (!stylesheet || !data) {
      return '';
    }

    var parser = new DOMParser();
    var parsedData = parser.parseFromString(data, 'text/xml');
    source = stylesheet;
    new Ajax.Request(source, {
      asynchronous: false,
      method: 'get',
      onSuccess: function (transport) {
        xsl = transport.responseText;
      }.bind(this),
      onFailure: function (transport) {
        ORYX.Log.error('XSL load failed' + transport);
      }.bind(this),
    });
    var xsltProcessor = new XSLTProcessor();
    var domParser = new DOMParser();
    var xslObject = domParser.parseFromString(xsl, 'text/xml');
    xsltProcessor.importStylesheet(xslObject);

    try {
      var newData = xsltProcessor.transformToFragment(parsedData, document);
      var serializedData = new XMLSerializer().serializeToString(newData);

      /* Firefox 2 to 3 problem?! */
      serializedData = !serializedData.startsWith('<?xml') ? '<?xml version="1.0" encoding="UTF-8"?>' + serializedData : serializedData;

      return serializedData;
    } catch (error) {
      return -1;
    }
  },

  /**
   * Opens a new window that shows the given XML content.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @param {Object} content The XML content to be shown.
   * @example
   * openDownloadWindow( "my.xml", "<exampleXML />" );
   */
  openXMLWindow: function (content) {
    var win = window.open(
      'data:application/xml,' + encodeURIComponent(content),
      '_blank',
      'resizable=yes,width=600,height=600,toolbar=0,scrollbars=yes'
    );
  },

  /**
   * Opens a download window for downloading the given content.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @param {String} filename The content's file name
   * @param {String} content The content to download
   */
  openDownloadWindow: function (filename, content) {
    var win = window.open('');
    if (win != null) {
      win.document.open();
      win.document.write('<html><body>');
      var submitForm = win.document.createElement('form');
      win.document.body.appendChild(submitForm);

      var createHiddenElement = function (name, value) {
        var newElement = document.createElement('input');
        newElement.name = name;
        newElement.type = 'hidden';
        newElement.value = value;
        return newElement;
      };

      submitForm.appendChild(createHiddenElement('download', content));
      submitForm.appendChild(createHiddenElement('file', filename));

      submitForm.method = 'POST';
      win.document.write('</body></html>');
      win.document.close();
      submitForm.action = ORYX.PATH + '/download';
      submitForm.submit();
    }
  },

  /**
   * Serializes DOM.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @type {String} Serialized DOM
   */
  getSerializedDOM: function () {
    // Force to set all resource IDs
    var serializedDOM = DataManager.serializeDOM(this.facade);

    //add namespaces
    serializedDOM =
      '<?xml version="1.0" encoding="utf-8"?>' +
      '<html xmlns="http://www.w3.org/1999/xhtml" ' +
      'xmlns:b3mn="http://b3mn.org/2007/b3mn" ' +
      'xmlns:ext="http://b3mn.org/2007/ext" ' +
      'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ' +
      'xmlns:atom="http://b3mn.org/2007/atom+xhtml">' +
      '<head profile="http://purl.org/NET/erdf/profile">' +
      '<link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />' +
      '<link rel="schema.dcTerms" href="http://purl.org/dc/terms/ " />' +
      '<link rel="schema.b3mn" href="http://b3mn.org" />' +
      '<link rel="schema.oryx" href="http://oryx-editor.org/" />' +
      '<link rel="schema.raziel" href="http://raziel.org/" />' +
      '<base href="' +
      location.href.split('?')[0] +
      '" />' +
      '</head><body>' +
      serializedDOM +
      '</body></html>';

    return serializedDOM;
  },

  /**
   * Sets the editor in read only mode: Edges/ dockers cannot be moved anymore,
   * shapes cannot be selected anymore.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   */
  enableReadOnlyMode: function () {
    //Edges cannot be moved anymore
    this.facade.disableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN);

    // Stop the user from editing the diagram while the plugin is active
    this._stopSelectionChange = function () {
      if (this.facade.getSelection().length > 0) {
        this.facade.setSelection([]);
      }
    };
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this));
  },
  /**
   * Disables read only mode, see @see
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @see ORYX.Plugins.AbstractPlugin.prototype.enableReadOnlyMode
   */
  disableReadOnlyMode: function () {
    // Edges can be moved now again
    this.facade.enableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN);

    if (this._stopSelectionChange) {
      this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this));
      this._stopSelectionChange = undefined;
    }
  },

  /**
   * Extracts RDF from DOM.
   * @methodOf ORYX.Plugins.AbstractPlugin.prototype
   * @type {String} Extracted RFD. Null if there are transformation errors.
   */
  getRDFFromDOM: function () {
    //convert to RDF
    try {
      var xsl = '';
      source = ORYX.PATH + 'lib/extract-rdf.xsl';
      new Ajax.Request(source, {
        asynchronous: false,
        method: 'get',
        onSuccess: function (transport) {
          xsl = transport.responseText;
        }.bind(this),
        onFailure: function (transport) {
          ORYX.Log.error('XSL load failed' + transport);
        }.bind(this),
      });

      var domParser = new DOMParser();
      var xmlObject = domParser.parseFromString(this.getSerializedDOM(), 'text/xml');
      var xslObject = domParser.parseFromString(xsl, 'text/xml');
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(xslObject);
      var result = xsltProcessor.transformToFragment(xmlObject, document);

      var serializer = new XMLSerializer();

      return serializer.serializeToString(result);
    } catch (e) {
      console.log('error serializing ' + e);
      return '';
    }
  },

  /**
   * Checks if a certain stencil set is loaded right now.
   *
   */
  isStencilSetExtensionLoaded: function (stencilSetExtensionNamespace) {
    return this.facade
      .getStencilSets()
      .values()
      .any(
        function (ss) {
          return ss
            .extensions()
            .keys()
            .any(
              function (extensionKey) {
                return extensionKey == stencilSetExtensionNamespace;
              }.bind(this)
            );
        }.bind(this)
      );
  },

  /**
   * Raises an event so that registered layouters does
   * have the posiblility to layout the given shapes
   * For further reading, have a look into the AbstractLayouter
   * class
   * @param {Object} shapes
   */
  doLayout: function (shapes) {
    // Raises a do layout event
    if (this.facade.raiseEvent) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_LAYOUT,
        shapes: shapes,
      });
    } else {
      this.facade.handleEvents({
        type: ORYX.CONFIG.EVENT_LAYOUT,
        shapes: shapes,
      });
    }
  },

  /**
   * Does a primitive layouting with the incoming/outgoing
   * edges (set the dockers to the right position) and if
   * necessary, it will be called the real layouting
   * @param {ORYX.Core.Node} node
   * @param {Array} edges
   */
  layoutEdges: function (node, allEdges, offset) {
    if (!this.facade.isExecutingCommands()) {
      return;
    }

    var Command = ORYX.Core.Command.extend({
      construct: function (edges, node, offset, plugin) {
        this.edges = edges;
        this.node = node;
        this.plugin = plugin;
        this.offset = offset;

        // Get the new absolute center
        var center = node.absoluteXY();
        this.ulo = { x: center.x - offset.x, y: center.y - offset.y };
      },
      execute: function () {
        if (this.changes) {
          this.executeAgain();
          return;
        } else {
          this.changes = [];
          this.edges.each(
            function (edge) {
              this.changes.push({
                edge: edge,
                oldDockerPositions: edge.dockers.map(function (r) {
                  return r.bounds.center();
                }),
              });
            }.bind(this)
          );
        }

        // Find all edges, which are related to the node and
        // have more than two dockers
        this.edges
          // Find all edges with more than two dockers
          .findAll(
            function (r) {
              return r.dockers.length > 2;
            }.bind(this)
          )
          // For every edge, check second and one before last docker
          // if there are horizontal/vertical on the same level
          // and if so, align the the bounds
          .each(
            function (edge) {
              if (edge.dockers.first().getDockedShape() === this.node) {
                var second = edge.dockers[1];
                if (this.align(second.bounds, edge.dockers.first())) {
                  second.update();
                }
              } else if (edge.dockers.last().getDockedShape() === this.node) {
                var beforeLast = edge.dockers[edge.dockers.length - 2];
                if (this.align(beforeLast.bounds, edge.dockers.last())) {
                  beforeLast.update();
                }
              }
              edge._update(true);
              edge.removeUnusedDockers();
              if (this.isBendPointIncluded(edge)) {
                this.plugin.doLayout(edge);
                return;
              }
            }.bind(this)
          );

        // Find all edges, which have only to dockers
        // and is located horizontal/vertical.
        // Do layout with those edges
        this.edges
          // Find all edges with exactly two dockers
          .each(
            function (edge) {
              if (edge.dockers.length == 2) {
                var p1 = edge.dockers.first().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center();
                var p2 = edge.dockers.last().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center();
                // Find all horizontal/vertical edges
                if (
                  Math.abs(-Math.abs(p1.x - p2.x) + Math.abs(this.offset.x)) < 2 ||
                  Math.abs(-Math.abs(p1.y - p2.y) + Math.abs(this.offset.y)) < 2
                ) {
                  this.plugin.doLayout(edge);
                }
              }
            }.bind(this)
          );

        this.edges.each(
          function (edge, i) {
            this.changes[i].dockerPositions = edge.dockers.map(function (r) {
              return r.bounds.center();
            });
          }.bind(this)
        );
      },
      /**
       * Align the bounds if the center is
       * the same than the old center
       * @params {Object} bounds
       * @params {Object} bounds2
       */
      align: function (bounds, refDocker) {
        var abRef = refDocker.getAbsoluteReferencePoint() || refDocker.bounds.center();

        var xdif = bounds.center().x - abRef.x;
        var ydif = bounds.center().y - abRef.y;
        if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3 && this.offset.xs === undefined) {
          bounds.moveBy({ x: -xdif, y: 0 });
        }
        if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3 && this.offset.ys === undefined) {
          bounds.moveBy({ y: -ydif, x: 0 });
        }

        if (this.offset.xs !== undefined || this.offset.ys !== undefined) {
          var absPXY = refDocker.getDockedShape().absoluteXY();
          xdif = bounds.center().x - (absPXY.x + (abRef.x - absPXY.x) / this.offset.xs);
          ydif = bounds.center().y - (absPXY.y + (abRef.y - absPXY.y) / this.offset.ys);

          if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3) {
            bounds.moveBy({ x: -(bounds.center().x - abRef.x), y: 0 });
          }

          if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3) {
            bounds.moveBy({ y: -(bounds.center().y - abRef.y), x: 0 });
          }
        }
      },

      /**
       * Returns a TRUE if there are bend point which overlay the shape
       */
      isBendPointIncluded: function (edge) {
        // Get absolute bounds
        var ab = edge.dockers.first().getDockedShape();
        var bb = edge.dockers.last().getDockedShape();

        if (ab) {
          ab = ab.absoluteBounds();
          ab.widen(5);
        }

        if (bb) {
          bb = bb.absoluteBounds();
          bb.widen(20); // Wide with 20 because of the arrow from the edge
        }

        return edge.dockers.any(function (docker, i) {
          var c = docker.bounds.center();
          // Dont count first and last
          return (
            i != 0 &&
            i != edge.dockers.length - 1 &&
            // Check if the point is included to the absolute bounds
            ((ab && ab.isIncluded(c)) || (bb && bb.isIncluded(c)))
          );
        });
      },

      removeAllDocker: function (edge) {
        edge.dockers.slice(1, edge.dockers.length - 1).each(function (docker) {
          edge.removeDocker(docker);
        });
      },
      executeAgain: function () {
        this.changes.each(
          function (change) {
            // Reset the dockers
            this.removeAllDocker(change.edge);
            change.dockerPositions.each(
              function (pos, i) {
                if (i == 0 || i == change.dockerPositions.length - 1) {
                  return;
                }
                var docker = change.edge.createDocker(undefined, pos);
                docker.bounds.centerMoveTo(pos);
                docker.update();
              }.bind(this)
            );
            change.edge._update(true);
          }.bind(this)
        );
      },
      rollback: function () {
        this.changes.each(
          function (change) {
            // Reset the dockers
            this.removeAllDocker(change.edge);
            change.oldDockerPositions.each(
              function (pos, i) {
                if (i == 0 || i == change.oldDockerPositions.length - 1) {
                  return;
                }
                var docker = change.edge.createDocker(undefined, pos);
                docker.bounds.centerMoveTo(pos);
                docker.update();
              }.bind(this)
            );
            change.edge._update(true);
          }.bind(this)
        );
      },
    });

    this.facade.executeCommands([new Command(allEdges, node, offset, this)]);
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX) {
  var ORYX = {};
}
if (!ORYX.Plugins) {
  ORYX.Plugins = {};
}

/**
   This abstract plugin implements the core behaviour of layout
   
   @class ORYX.Plugins.AbstractLayouter
   @constructor Creates a new instance
   @author Willi Tscheschner
*/
ORYX.Plugins.AbstractLayouter = ORYX.Plugins.AbstractPlugin.extend({
  /**
   * 'layouted' defined all types of shapes which will be layouted.
   * It can be one value or an array of values. The value
   * can be a Stencil ID (as String) or an class type of either
   * a ORYX.Core.Node or ORYX.Core.Edge
   * @type Array|String|Object
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  layouted: [],

  /**
   * Constructor
   * @param {Object} facade
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  construct: function (facade) {
    arguments.callee.$.construct.apply(this, arguments);

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LAYOUT, this._initLayout.bind(this));
  },

  /**
   * Proofs if this shape should be layouted or not
   * @param {Object} shape
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  isIncludedInLayout: function (shape) {
    if (!(this.layouted instanceof Array)) {
      this.layouted = [this.layouted].compact();
    }

    // If there are no elements
    if (this.layouted.length <= 0) {
      // Return TRUE
      return true;
    }

    // Return TRUE if there is any correlation between
    // the 'layouted' attribute and the shape themselve.
    return this.layouted.any(function (s) {
      if (typeof s == 'string') {
        return shape.getStencil().id().include(s);
      } else {
        return shape instanceof s;
      }
    });
  },

  /**
   * Callback to start the layouting
   * @param {Object} event Layout event
   * @param {Object} shapes Given shapes
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  _initLayout: function (event) {
    // Get the shapes
    var shapes = [event.shapes].flatten().compact();

    // Find all shapes which should be layouted
    var toLayout = shapes.findAll(
      function (shape) {
        return this.isIncludedInLayout(shape);
      }.bind(this)
    );

    // If there are shapes left
    if (toLayout.length > 0) {
      // Do layout
      this.layout(toLayout);
    }
  },

  /**
   * Implementation of layouting a set on shapes
   * @param {Object} shapes Given shapes
   * @memberOf ORYX.Plugins.AbstractLayouter.prototype
   */
  layout: function (shapes) {
    throw new Error('Layouter has to implement the layout function.');
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.Edit = Clazz.extend({
  construct: function (facade) {
    this.facade = facade;
    this.clipboard = new ORYX.Plugins.Edit.ClipBoard();

    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));

    this.facade.offer({
      name: ORYX.I18N.Edit.cut,
      description: ORYX.I18N.Edit.cutDesc,
      icon: ORYX.PATH + 'images/cut.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 88,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.callEdit.bind(this, this.editCut),
      group: ORYX.I18N.Edit.group,
      index: 1,
      minShape: 1,
    });

    this.facade.offer({
      name: ORYX.I18N.Edit.copy,
      description: ORYX.I18N.Edit.copyDesc,
      icon: ORYX.PATH + 'images/page_copy.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 67,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.callEdit.bind(this, this.editCopy, [true, false]),
      group: ORYX.I18N.Edit.group,
      index: 2,
      minShape: 1,
    });

    this.facade.offer({
      name: ORYX.I18N.Edit.paste,
      description: ORYX.I18N.Edit.pasteDesc,
      icon: ORYX.PATH + 'images/page_paste.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 86,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.callEdit.bind(this, this.editPaste),
      isEnabled: this.clipboard.isOccupied.bind(this.clipboard),
      group: ORYX.I18N.Edit.group,
      index: 3,
      minShape: 0,
      maxShape: 0,
    });

    this.facade.offer({
      name: ORYX.I18N.Edit.del,
      description: ORYX.I18N.Edit.delDesc,
      icon: ORYX.PATH + 'images/cross.png',
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 8,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
        {
          keyCode: 46,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.callEdit.bind(this, this.editDelete),
      group: ORYX.I18N.Edit.group,
      index: 4,
      minShape: 1,
    });
  },

  callEdit: function (fn, args) {
    window.setTimeout(
      function () {
        fn.apply(this, args instanceof Array ? args : []);
      }.bind(this),
      1
    );
  },

  /**
   * Handles the mouse down event and starts the copy-move-paste action, if
   * control or meta key is pressed.
   */
  handleMouseDown: function (event) {
    if (this._controlPressed) {
      this._controlPressed = false;
      this.editCopy();
      //			console.log("copiedEle: %0",this.clipboard.shapesAsJson)
      //			console.log("mousevent: %o",event)
      this.editPaste();
      event.forceExecution = true;
      this.facade.raiseEvent(event, this.clipboard.shapesAsJson);
    }
  },

  /**
   * The key handler for this plugin. Every action from the set of cut, copy,
   * paste and delete should be accessible trough simple keyboard shortcuts.
   * This method checks whether any event triggers one of those actions.
   *
   * @param {Object} event The keyboard event that should be analysed for
   *     triggering of this plugin.
   */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //
  //        ORYX.Log.debug("edit.js handles a keyEvent.");
  //
  //        // assure we have the current event.
  //        if (!event)
  //            event = window.event;
  //
  //
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //        // if the object is to be deleted, do so, and return immediately.
  //        if ((pressedKey == ORYX.CONFIG.KEY_CODE_DELETE) ||
  //        ((pressedKey == ORYX.CONFIG.KEY_CODE_BACKSPACE) &&
  //        (event.metaKey || event.appleMetaKey))) {
  //
  //            ORYX.Log.debug("edit.js deletes the shape.");
  //            this.editDelete();
  //            return;
  //        }
  //
  //         // if control key is not pressed, we're not interested anymore.
  //         if (!ctrlPressed)
  //         return;
  //
  //         // when ctrl is pressed, switch trough the possibilities.
  //         switch (pressedKey) {
  //
  //	         // cut.
  //	         case ORYX.CONFIG.KEY_CODE_X:
  //	         this.editCut();
  //	         break;
  //
  //	         // copy.
  //	         case ORYX.CONFIG.KEY_CODE_C:
  //	         this.editCopy();
  //	         break;
  //
  //	         // paste.
  //	         case ORYX.CONFIG.KEY_CODE_V:
  //	         this.editPaste();
  //	         break;
  //         }
  //    },

  /**
   * Returns a list of shapes which should be considered while copying.
   * Besides the shapes of given ones, edges and attached nodes are added to the result set.
   * If one of the given shape is a child of another given shape, it is not put into the result.
   */
  getAllShapesToConsider: function (shapes) {
    var shapesToConsider = []; // only top-level shapes
    var childShapesToConsider = []; // all child shapes of top-level shapes

    shapes.each(
      function (shape) {
        //Throw away these shapes which have a parent in given shapes
        isChildShapeOfAnother = shapes.any(function (s2) {
          return s2.hasChildShape(shape);
        });
        if (isChildShapeOfAnother) return;

        // This shape should be considered
        shapesToConsider.push(shape);
        // Consider attached nodes (e.g. intermediate events)
        if (shape instanceof ORYX.Core.Node) {
          var attached = shape.getOutgoingNodes();
          attached = attached.findAll(function (a) {
            return !shapes.include(a);
          });
          shapesToConsider = shapesToConsider.concat(attached);
        }

        childShapesToConsider = childShapesToConsider.concat(shape.getChildShapes(true));
      }.bind(this)
    );

    // All edges between considered child shapes should be considered
    // Look for these edges having incoming and outgoing in childShapesToConsider
    var edgesToConsider = this.facade
      .getCanvas()
      .getChildEdges()
      .select(function (edge) {
        // Ignore if already added
        if (shapesToConsider.include(edge)) return false;
        // Ignore if there are no docked shapes
        if (edge.getAllDockedShapes().size() === 0) return false;
        // True if all docked shapes are in considered child shapes
        return edge.getAllDockedShapes().all(function (shape) {
          // Remember: Edges can have other edges on outgoing, that is why edges must not be included in childShapesToConsider
          return shape instanceof ORYX.Core.Edge || childShapesToConsider.include(shape);
        });
      });
    shapesToConsider = shapesToConsider.concat(edgesToConsider);

    return shapesToConsider;
  },

  /**
   * Performs the cut operation by first copy-ing and then deleting the
   * current selection.
   */
  editCut: function () {
    //TODO document why this returns false.
    //TODO document what the magic boolean parameters are supposed to do.

    this.editCopy(false, true);
    this.editDelete(true);
    return false;
  },

  /**
   * Performs the copy operation.
   * @param {Object} will_not_update ??
   */
  editCopy: function (will_update, useNoOffset) {
    var selection = this.facade.getSelection();

    //if the selection is empty, do not remove the previously copied elements
    if (selection.length == 0) return;

    this.clipboard.refresh(
      selection,
      this.getAllShapesToConsider(selection),
      this.facade.getCanvas().getStencil().stencilSet().namespace(),
      useNoOffset
    );

    if (will_update) this.facade.updateSelection();
  },

  /**
   * Performs the paste operation.
   */
  editPaste: function () {
    // Create a new canvas with childShapes
    //and stencilset namespace to be JSON Import conform
    var canvas = {
      childShapes: this.clipboard.shapesAsJson,
      stencilset: {
        namespace: this.clipboard.SSnamespace,
      },
    };
    // Apply json helper to iterate over json object
    jQuery.extend(canvas, ORYX.Core.AbstractShape.JSONHelper);

    var childShapeResourceIds = canvas.getChildShapes(true).pluck('resourceId');
    var outgoings = {};
    // Iterate over all shapes
    canvas.eachChild(
      function (shape, parent) {
        // Throw away these references where referenced shape isn't copied
        shape.outgoing = shape.outgoing.select(function (out) {
          return childShapeResourceIds.include(out.resourceId);
        });
        shape.outgoing.each(function (out) {
          if (!outgoings[out.resourceId]) {
            outgoings[out.resourceId] = [];
          }
          outgoings[out.resourceId].push(shape);
        });

        return shape;
      }.bind(this),
      true,
      true
    );

    // Iterate over all shapes
    canvas.eachChild(
      function (shape, parent) {
        // Check if there has a valid target
        if (shape.target && !childShapeResourceIds.include(shape.target.resourceId)) {
          shape.target = undefined;
          shape.targetRemoved = true;
        }

        // Check if the first docker is removed
        if (
          shape.dockers &&
          shape.dockers.length >= 1 &&
          shape.dockers[0].getDocker &&
          ((shape.dockers[0].getDocker().getDockedShape() &&
            !childShapeResourceIds.include(shape.dockers[0].getDocker().getDockedShape().resourceId)) ||
            (!shape.getShape().dockers[0].getDockedShape() && !outgoings[shape.resourceId]))
        ) {
          shape.sourceRemoved = true;
        }

        return shape;
      }.bind(this),
      true,
      true
    );

    // Iterate over top-level shapes
    canvas.eachChild(
      function (shape, parent) {
        // All top-level shapes should get an offset in their bounds
        // Move the shape occording to COPY_MOVE_OFFSET
        if (this.clipboard.useOffset) {
          shape.bounds = {
            lowerRight: {
              x: shape.bounds.lowerRight.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
              y: shape.bounds.lowerRight.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
            },
            upperLeft: {
              x: shape.bounds.upperLeft.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
              y: shape.bounds.upperLeft.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
            },
          };
        }
        // Only apply offset to shapes with a target
        if (shape.dockers) {
          shape.dockers = shape.dockers.map(
            function (docker, i) {
              // If shape had a target but the copied does not have anyone anymore,
              // migrate the relative dockers to absolute ones.
              if (
                (shape.targetRemoved === true && i == shape.dockers.length - 1 && docker.getDocker) ||
                (shape.sourceRemoved === true && i == 0 && docker.getDocker)
              ) {
                docker = docker.getDocker().bounds.center();
              }

              // If it is the first docker and it has a docked shape,
              // just return the coordinates
              if (
                (i == 0 &&
                  docker.getDocker instanceof Function &&
                  shape.sourceRemoved !== true &&
                  (docker.getDocker().getDockedShape() ||
                    ((outgoings[shape.resourceId] || []).length > 0 &&
                      (!(shape.getShape() instanceof ORYX.Core.Node) ||
                        outgoings[shape.resourceId][0].getShape() instanceof ORYX.Core.Node)))) ||
                (i == shape.dockers.length - 1 &&
                  docker.getDocker instanceof Function &&
                  shape.targetRemoved !== true &&
                  (docker.getDocker().getDockedShape() || shape.target))
              ) {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker,
                };
              } else if (this.clipboard.useOffset) {
                return {
                  x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  getDocker: docker.getDocker,
                };
              } else {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker,
                };
              }
            }.bind(this)
          );
        } else if (
          shape.getShape() instanceof ORYX.Core.Node &&
          shape.dockers &&
          shape.dockers.length > 0 &&
          (!shape.dockers.first().getDocker ||
            shape.sourceRemoved === true ||
            !(shape.dockers.first().getDocker().getDockedShape() || outgoings[shape.resourceId]))
        ) {
          shape.dockers = shape.dockers.map(
            function (docker, i) {
              if (shape.sourceRemoved === true && i == 0 && docker.getDocker) {
                docker = docker.getDocker().bounds.center();
              }

              if (this.clipboard.useOffset) {
                return {
                  x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
                  getDocker: docker.getDocker,
                };
              } else {
                return {
                  x: docker.x,
                  y: docker.y,
                  getDocker: docker.getDocker,
                };
              }
            }.bind(this)
          );
        }

        return shape;
      }.bind(this),
      false,
      true
    );

    this.clipboard.useOffset = true;
    this.facade.importJSON(canvas);
  },

  /**
   * Performs the delete operation. No more asking.
   */
  editDelete: function () {
    var selection = this.facade.getSelection();

    var clipboard = new ORYX.Plugins.Edit.ClipBoard();
    clipboard.refresh(selection, this.getAllShapesToConsider(selection));

    var command = new ORYX.Plugins.Edit.DeleteCommand(clipboard, this.facade);

    this.facade.executeCommands([command]);
  },
});

ORYX.Plugins.Edit.ClipBoard = Clazz.extend({
  construct: function () {
    this.shapesAsJson = [];
    this.selection = [];
    this.SSnamespace = '';
    this.useOffset = true;
  },
  isOccupied: function () {
    return this.shapesAsJson.length > 0;
  },
  refresh: function (selection, shapes, namespace, useNoOffset) {
    this.selection = selection;
    this.SSnamespace = namespace;
    // Store outgoings, targets and parents to restore them later on
    this.outgoings = {};
    this.parents = {};
    this.targets = {};
    this.useOffset = useNoOffset !== true;

    this.shapesAsJson = shapes.map(function (shape) {
      var s = shape.toJSON();
      s.parent = { resourceId: shape.getParentShape().resourceId };
      s.parentIndex = shape.getParentShape().getChildShapes().indexOf(shape);
      return s;
    });
  },
});

ORYX.Plugins.Edit.DeleteCommand = ORYX.Core.Command.extend({
  construct: function (clipboard, facade) {
    this.clipboard = clipboard;
    this.shapesAsJson = clipboard.shapesAsJson;
    this.facade = facade;

    // Store dockers of deleted shapes to restore connections
    this.dockers = this.shapesAsJson
      .map(function (shapeAsJson) {
        var shape = shapeAsJson.getShape();
        var incomingDockers = shape.getIncomingShapes().map(function (s) {
          return s.getDockers().last();
        });
        var outgoingDockers = shape.getOutgoingShapes().map(function (s) {
          return s.getDockers().first();
        });
        var dockers = shape
          .getDockers()
          .concat(incomingDockers, outgoingDockers)
          .compact()
          .map(function (docker) {
            return {
              object: docker,
              referencePoint: docker.referencePoint,
              dockedShape: docker.getDockedShape(),
            };
          });
        return dockers;
      })
      .flatten();
  },
  execute: function () {
    this.shapesAsJson.each(
      function (shapeAsJson) {
        // Delete shape
        this.facade.deleteShape(shapeAsJson.getShape());
      }.bind(this)
    );

    this.facade.setSelection([]);
    this.facade.getCanvas().update();
    this.facade.updateSelection();
  },
  rollback: function () {
    this.shapesAsJson.each(
      function (shapeAsJson) {
        var shape = shapeAsJson.getShape();
        var parent = this.facade.getCanvas().getChildShapeByResourceId(shapeAsJson.parent.resourceId) || this.facade.getCanvas();
        parent.add(shape, shape.parentIndex);
      }.bind(this)
    );

    //reconnect shapes
    this.dockers.each(
      function (d) {
        d.object.setDockedShape(d.dockedShape);
        d.object.setReferencePoint(d.referencePoint);
      }.bind(this)
    );

    this.facade.setSelection(this.selectedShapes);
    this.facade.getCanvas().update();
    this.facade.updateSelection();
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * @namespace Oryx name space for plugins
 * @name ORYX.Plugins
 */
if (!ORYX.Plugins) ORYX.Plugins = new Object();

/**
 * The view plugin offers all of zooming functionality accessible over the
 * tool bar. This are zoom in, zoom out, zoom to standard, zoom fit to model.
 *
 * @class ORYX.Plugins.View
 * @extends Clazz
 * @param {Object} facade The editor facade for plugins.
 */
ORYX.Plugins.View = {
  /** @lends ORYX.Plugins.View.prototype */
  facade: undefined,

  construct: function (facade, ownPluginData) {
    this.facade = facade;
    //Standard Values
    this.zoomLevel = 1.0;
    this.maxFitToScreenLevel = 1.5;
    this.minZoomLevel = 0.1;
    this.maxZoomLevel = 2.5;
    this.diff = 5; //difference between canvas and view port, s.th. like toolbar??

    //Read properties
    if (ownPluginData !== undefined && ownPluginData !== null) {
      ownPluginData.properties.each(
        function (property) {
          if (property.zoomLevel) {
            this.zoomLevel = Number(1.0);
          }
          if (property.maxFitToScreenLevel) {
            this.maxFitToScreenLevel = Number(property.maxFitToScreenLevel);
          }
          if (property.minZoomLevel) {
            this.minZoomLevel = Number(property.minZoomLevel);
          }
          if (property.maxZoomLevel) {
            this.maxZoomLevel = Number(property.maxZoomLevel);
          }
        }.bind(this)
      );
    }

    /* Register zoom in */
    this.facade.offer({
      name: ORYX.I18N.View.zoomIn,
      functionality: this.zoom.bind(this, [1.0 + ORYX.CONFIG.ZOOM_OFFSET]),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/magnifier_zoom_in.png',
      description: ORYX.I18N.View.zoomInDesc,
      index: 1,
      minShape: 0,
      maxShape: 0,
      isEnabled: function () {
        return this.zoomLevel < this.maxZoomLevel;
      }.bind(this),
    });

    /* Register zoom out */
    this.facade.offer({
      name: ORYX.I18N.View.zoomOut,
      functionality: this.zoom.bind(this, [1.0 - ORYX.CONFIG.ZOOM_OFFSET]),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/magnifier_zoom_out.png',
      description: ORYX.I18N.View.zoomOutDesc,
      index: 2,
      minShape: 0,
      maxShape: 0,
      isEnabled: function () {
        return this._checkSize();
      }.bind(this),
    });

    /* Register zoom standard */
    this.facade.offer({
      name: ORYX.I18N.View.zoomStandard,
      functionality: this.setAFixZoomLevel.bind(this, 1),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/zoom_standard.png',
      cls: 'icon-large',
      description: ORYX.I18N.View.zoomStandardDesc,
      index: 3,
      minShape: 0,
      maxShape: 0,
      isEnabled: function () {
        return this.zoomLevel != 1;
      }.bind(this),
    });

    /* Register zoom fit to model */
    this.facade.offer({
      name: ORYX.I18N.View.zoomFitToModel,
      functionality: this.zoomFitToModel.bind(this),
      group: ORYX.I18N.View.group,
      icon: ORYX.PATH + 'images/image.png',
      description: ORYX.I18N.View.zoomFitToModelDesc,
      index: 4,
      minShape: 0,
      maxShape: 0,
    });
  },

  /**
   * It sets the zoom level to a fix value and call the zooming function.
   *
   * @param {Number} zoomLevel
   * 			the zoom level
   */
  setAFixZoomLevel: function (zoomLevel) {
    this.zoomLevel = zoomLevel;
    this._checkZoomLevelRange();
    this.zoom(1);
  },

  /**
   * It does the actual zooming. It changes the viewable size of the canvas
   * and all to its child elements.
   *
   * @param {Number} factor
   * 		the factor to adjust the zoom level
   */
  zoom: function (factor) {
    // TODO: Zoomen auf allen Objekten im SVG-DOM

    this.zoomLevel *= factor;
    var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode;
    var canvas = this.facade.getCanvas();
    var newWidth = canvas.bounds.width() * this.zoomLevel;
    var newHeight = canvas.bounds.height() * this.zoomLevel;

    /* Set new top offset */
    var offsetTop = (canvas.node.parentNode.parentNode.parentNode.offsetHeight - newHeight) / 2.0;
    offsetTop = offsetTop > 20 ? offsetTop - 20 : 0;
    canvas.node.parentNode.parentNode.style.marginTop = offsetTop + 'px';
    offsetTop += 5;
    canvas.getHTMLContainer().style.top = offsetTop + 'px';

    /*readjust scrollbar*/
    var newScrollTop = scrollNode.scrollTop - Math.round((canvas.getHTMLContainer().parentNode.getHeight() - newHeight) / 2) + this.diff;
    var newScrollLeft = scrollNode.scrollLeft - Math.round((canvas.getHTMLContainer().parentNode.getWidth() - newWidth) / 2) + this.diff;

    /* Set new Zoom-Level */
    canvas.setSize({ width: newWidth, height: newHeight }, true);

    /* Set Scale-Factor */
    canvas.node.setAttributeNS(null, 'transform', 'scale(' + this.zoomLevel + ')');

    /* Refresh the Selection */
    this.facade.updateSelection();
    scrollNode.scrollTop = newScrollTop;
    scrollNode.scrollLeft = newScrollLeft;

    /* Update the zoom-level*/
    canvas.zoomLevel = this.zoomLevel;
  },

  /**
   * It calculates the zoom level to fit whole model into the visible area
   * of the canvas. Than the model gets zoomed and the position of the
   * scroll bars are adjusted.
   *
   */
  zoomFitToModel: function () {
    /* Get the size of the visible area of the canvas */
    var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode;
    var visibleHeight = scrollNode.getHeight() - 30;
    var visibleWidth = scrollNode.getWidth() - 30;

    var nodes = this.facade.getCanvas().getChildShapes();

    if (!nodes || nodes.length < 1) {
      return false;
    }

    /* Calculate size of canvas to fit the model */
    var bounds = nodes[0].absoluteBounds().clone();
    nodes.each(function (node) {
      bounds.include(node.absoluteBounds().clone());
    });

    /* Set new Zoom Level */
    var scaleFactorWidth = visibleWidth / bounds.width();
    var scaleFactorHeight = visibleHeight / bounds.height();

    /* Choose the smaller zoom level to fit the whole model */
    var zoomFactor = scaleFactorHeight < scaleFactorWidth ? scaleFactorHeight : scaleFactorWidth;

    /*Test if maximum zoom is reached*/
    if (zoomFactor > this.maxFitToScreenLevel) {
      zoomFactor = this.maxFitToScreenLevel;
    }
    /* Do zooming */
    this.setAFixZoomLevel(zoomFactor);

    /* Set scroll bar position */
    scrollNode.scrollTop = Math.round(bounds.upperLeft().y * this.zoomLevel) - 5;
    scrollNode.scrollLeft = Math.round(bounds.upperLeft().x * this.zoomLevel) - 5;
  },

  /**
   * It checks if the zoom level is less or equal to the level, which is required
   * to schow the whole canvas.
   *
   * @private
   */
  _checkSize: function () {
    var canvasParent = this.facade.getCanvas().getHTMLContainer().parentNode;
    var minForCanvas = Math.min(
      canvasParent.parentNode.getWidth() / canvasParent.getWidth(),
      canvasParent.parentNode.getHeight() / canvasParent.getHeight()
    );
    return 1.05 > minForCanvas;
  },
  /**
   * It checks if the zoom level is included in the definined zoom
   * level range.
   *
   * @private
   */
  _checkZoomLevelRange: function () {
    /*var canvasParent=this.facade.getCanvas().getHTMLContainer().parentNode;
		var maxForCanvas= Math.max((canvasParent.parentNode.getWidth()/canvasParent.getWidth()),(canvasParent.parentNode.getHeight()/canvasParent.getHeight()));
		if(this.zoomLevel > maxForCanvas) {
			this.zoomLevel = maxForCanvas;			
		}*/
    if (this.zoomLevel < this.minZoomLevel) {
      this.zoomLevel = this.minZoomLevel;
    }

    if (this.zoomLevel > this.maxZoomLevel) {
      this.zoomLevel = this.maxZoomLevel;
    }
  },
};

ORYX.Plugins.View = Clazz.extend(ORYX.Plugins.View);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
if (!Signavio) {
  var Signavio = {};
}
if (!Signavio.Core) {
  Signavio.Core = {};
}
Signavio.Core.Version = '1.0';
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!Signavio) {
  var Signavio = new Object();
}

if (!Signavio.Plugins) {
  Signavio.Plugins = new Object();
}

if (!Signavio.Plugins.Utils) {
  Signavio.Plugins.Utils = new Object();
}

if (!Signavio.Helper) {
  Signavio.Helper = new Object();
}

new (function () {
  /**
   * Provides an uniq id
   * @overwrite
   * @return {String}
   *
   */
  ORYX.Editor.provideId = function () {
    var res = [],
      hex = '0123456789ABCDEF';

    for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random() * 0x10);

    res[14] = 4;
    res[19] = (res[19] & 0x3) | 0x8;

    for (var i = 0; i < 36; i++) res[i] = hex[res[i]];

    res[8] = res[13] = res[18] = res[23] = '-';

    return 'sid-' + res.join('');
  };
})();

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) {
  ORYX.Plugins = new Object();
}

/**
 * This plugin is responsible for displaying loading indicators and to prevent
 * the user from accidently unloading the page by, e.g., pressing the backspace
 * button and returning to the previous site in history.
 * @param {Object} facade The editor plugin facade to register enhancements with.
 */
ORYX.Plugins.Loading = {
  construct: function (facade) {
    this.facade = facade;

    // The parent Node
    this.node = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', this.facade.getCanvas().getHTMLContainer().parentNode, [
      'div',
      {
        class: 'LoadingIndicator',
      },
      '',
    ]);

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_ENABLE, this.enableLoading.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_DISABLE, this.disableLoading.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_STATUS, this.showStatus.bind(this));

    this.disableLoading();
  },

  enableLoading: function (options) {
    if (options.text) this.node.innerHTML = options.text + '...';
    else this.node.innerHTML = ORYX.I18N.Loading.waiting;
    this.node.removeClassName('StatusIndicator');
    this.node.addClassName('LoadingIndicator');
    this.node.style.display = 'block';

    var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode;

    this.node.style.top = pos.offsetTop + 'px';
    this.node.style.left = pos.offsetLeft + 'px';
  },

  disableLoading: function () {
    this.node.style.display = 'none';
  },

  showStatus: function (options) {
    if (options.text) {
      this.node.innerHTML = options.text;
      this.node.addClassName('StatusIndicator');
      this.node.removeClassName('LoadingIndicator');
      this.node.style.display = 'block';

      var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode;

      this.node.style.top = pos.offsetTop + 'px';
      this.node.style.left = pos.offsetLeft + 'px';

      var tout = options.timeout ? options.timeout : 2000;

      window.setTimeout(
        function () {
          this.disableLoading();
        }.bind(this),
        tout
      );
    }
  },
};

ORYX.Plugins.Loading = Clazz.extend(ORYX.Plugins.Loading);
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
if (!ORYX.Plugins) {
  ORYX.Plugins = new Object();
}

/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

/**
 * This plugin is responsible for resizing the canvas.
 * @param {Object} facade The editor plugin facade to register enhancements with.
 */
ORYX.Plugins.CanvasResize = Clazz.extend({
  construct: function (facade) {
    this.facade = facade;
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'N', this.resize.bind(this));
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'W', this.resize.bind(this));
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'E', this.resize.bind(this));
    new ORYX.Plugins.CanvasResizeButton(this.facade.getCanvas(), 'S', this.resize.bind(this));

    window.setTimeout(function () {
      jQuery(window).trigger('resize');
    });
  },

  resize: function (position, shrink) {
    resizeCanvas = function (position, extentionSize, facade) {
      var canvas = facade.getCanvas();
      var b = canvas.bounds;
      var scrollNode = facade.getCanvas().getHTMLContainer().parentNode.parentNode;

      if (position == 'E' || position == 'W') {
        canvas.setSize({ width: (b.width() + extentionSize) * canvas.zoomLevel, height: b.height() * canvas.zoomLevel });
      } else if (position == 'S' || position == 'N') {
        canvas.setSize({ width: b.width() * canvas.zoomLevel, height: (b.height() + extentionSize) * canvas.zoomLevel });
      }

      if (position == 'N' || position == 'W') {
        var move = position == 'N' ? { x: 0, y: extentionSize } : { x: extentionSize, y: 0 };

        // Move all children
        canvas.getChildNodes(false, function (shape) {
          shape.bounds.moveBy(move);
        });
        // Move all dockers, when the edge has at least one docked shape
        var edges = canvas.getChildEdges().findAll(function (edge) {
          return edge.getAllDockedShapes().length > 0;
        });
        var dockers = edges
          .collect(function (edge) {
            return edge.dockers.findAll(function (docker) {
              return !docker.getDockedShape();
            });
          })
          .flatten();
        dockers.each(function (docker) {
          docker.bounds.moveBy(move);
        });
      } else if (position == 'S') {
        scrollNode.scrollTop += extentionSize;
      } else if (position == 'E') {
        scrollNode.scrollLeft += extentionSize;
      }

      jQuery(window).trigger('resize');

      canvas.update();
      facade.updateSelection();
    };

    var commandClass = ORYX.Core.Command.extend({
      construct: function (position, extentionSize, facade) {
        this.position = position;
        this.extentionSize = extentionSize;
        this.facade = facade;
      },
      execute: function () {
        resizeCanvas(this.position, this.extentionSize, this.facade);
      },
      rollback: function () {
        resizeCanvas(this.position, -this.extentionSize, this.facade);
      },
      update: function () {},
    });

    var extentionSize = ORYX.CONFIG.CANVAS_RESIZE_INTERVAL;
    if (shrink) extentionSize = -extentionSize;
    var command = new commandClass(position, extentionSize, this.facade);

    this.facade.executeCommands([command]);
  },
});

ORYX.Plugins.CanvasResizeButton = Clazz.extend({
  construct: function (canvas, position, callback) {
    this.canvas = canvas;
    var parentNode = canvas.getHTMLContainer().parentNode;

    window.myParent = parentNode;

    var actualScrollNode = jQuery('#canvasSection')[0];
    var scrollNode = actualScrollNode;
    var canvasNode = jQuery('#canvasSection').find('.ORYX_Editor')[0];
    var svgRootNode = canvasNode.children[0];

    var iconClass = 'glyphicon glyphicon-chevron-';
    var iconClassShrink = 'glyphicon glyphicon-chevron-';
    if (position == 'N') {
      iconClass += 'up';
      iconClassShrink += 'down';
    } else if (position == 'S') {
      iconClass += 'down';
      iconClassShrink += 'up';
    } else if (position == 'E') {
      iconClass += 'right';
      iconClassShrink += 'left';
    } else if (position == 'W') {
      iconClass += 'left';
      iconClassShrink += 'right';
    }

    // The buttons
    var idGrow = 'canvas-shrink-' + position;
    var idShrink = 'canvas-grow-' + position;

    var buttonGrow = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', parentNode, [
      'div',
      {
        class: 'canvas_resize_indicator canvas_resize_indicator_grow' + ' ' + position,
        id: idGrow,
        title: ORYX.I18N.RESIZE.tipGrow + ORYX.I18N.RESIZE[position],
      },
      ['i', { class: iconClass }],
    ]);
    var buttonShrink = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', parentNode, [
      'div',
      {
        class: 'canvas_resize_indicator canvas_resize_indicator_shrink' + ' ' + position,
        id: idShrink,
        title: ORYX.I18N.RESIZE.tipGrow + ORYX.I18N.RESIZE[position],
      },
      ['i', { class: iconClassShrink }],
    ]);
    // Defines a callback which gives back
    // a boolean if the current mouse event
    // is over the particular button area
    var offSetWidth = 60;
    var isOverOffset = function (event) {
      var isOverButton =
        event.target.id.indexOf('canvas-shrink') != -1 ||
        event.target.id.indexOf('canvas-grow') != -1 ||
        event.target.parentNode.id.indexOf('canvas-shrink') != -1 ||
        event.target.parentNode.id.indexOf('canvas-grow') != -1;
      if (isOverButton) {
        if (
          event.target.id == idGrow ||
          event.target.id == idShrink ||
          event.target.parentNode.id == idGrow ||
          event.target.parentNode.id == idShrink
        ) {
          return true;
        } else {
          return false;
        }
      }

      if (
        event.target != parentNode &&
        event.target != scrollNode &&
        event.target != scrollNode.firstChild &&
        event.target != svgRootNode &&
        event.target != scrollNode
      ) {
        return false;
      }

      //if(inCanvas){offSetWidth=30}else{offSetWidth=30*2}
      //Safari work around
      var X = event.offsetX !== undefined ? event.offsetX : event.layerX;
      var Y = event.offsetY !== undefined ? event.offsetY : event.layerY;

      var canvasOffset = 0;
      if (canvasNode.clientWidth < actualScrollNode.clientWidth) {
        var widthDiff = actualScrollNode.clientWidth - canvasNode.clientWidth;
        canvasOffset = widthDiff / 2;
      }

      // Adjust to relative location to the actual viewport
      Y = Y - actualScrollNode.scrollTop;
      X = X - actualScrollNode.scrollLeft;

      if (position == 'N') {
        return Y < offSetWidth;
      } else if (position == 'W') {
        return X < offSetWidth + canvasOffset;
      } else if (position == 'E') {
        return actualScrollNode.clientWidth - X < offSetWidth + canvasOffset;
      } else if (position == 'S') {
        return actualScrollNode.clientHeight - Y < offSetWidth;
      }

      return false;
    };

    var showButtons = function () {
      buttonGrow.show();

      var w = canvas.bounds.width();
      var h = canvas.bounds.height();

      if (position == 'N' && h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT) buttonShrink.show();
      else if (position == 'E' && w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH) buttonShrink.show();
      else if (position == 'S' && h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT) buttonShrink.show();
      else if (position == 'W' && w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH) buttonShrink.show();
      else buttonShrink.hide();
    }.bind(this);

    var hideButtons = function () {
      buttonGrow.hide();
      buttonShrink.hide();
    };

    // If the mouse move is over the button area, show the button
    parentNode.parentNode.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEMOVE,
      function (event) {
        if (isOverOffset(event)) {
          showButtons();
        } else {
          hideButtons();
        }
      },
      false
    );
    // If the mouse is over the button, show them
    buttonGrow.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOVER,
      function (event) {
        showButtons();
      },
      true
    );
    buttonShrink.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOVER,
      function (event) {
        showButtons();
      },
      true
    );
    // If the mouse is out, hide the button
    //scrollNode.addEventListener(		ORYX.CONFIG.EVENT_MOUSEOUT, 	function(event){button.hide()}, true )
    parentNode.parentNode.addEventListener(
      ORYX.CONFIG.EVENT_MOUSEOUT,
      function (event) {
        hideButtons();
      },
      true
    );
    //svgRootNode.addEventListener(	ORYX.CONFIG.EVENT_MOUSEOUT, 	function(event){ inCanvas = false } , true );

    // Hide the button initialy
    hideButtons();

    // Add the callbacks
    buttonGrow.addEventListener(
      'click',
      function () {
        callback(position);
        showButtons();
      },
      true
    );
    buttonShrink.addEventListener(
      'click',
      function () {
        callback(position, true);
        showButtons();
      },
      true
    );
  },
});

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.RenameShapes = Clazz.extend({
  facade: undefined,

  construct: function (facade) {
    this.facade = facade;

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_CANVAS_SCROLL, this.hideField.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DBLCLICK, this.actOnDBLClick.bind(this));
    this.facade.offer({
      keyCodes: [
        {
          keyCode: 113, // F2-Key
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.renamePerF2.bind(this),
    });

    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.hide.bind(this), true);
  },

  /**
   * This method handles the "F2" key down event. The selected shape are looked
   * up and the editing of title/name of it gets started.
   */
  renamePerF2: function () {
    var selectedShapes = this.facade.getSelection();
    this.actOnDBLClick(undefined, selectedShapes.first());
  },

  actOnDBLClick: function (evt, shape) {
    if (!(shape instanceof ORYX.Core.Shape)) {
      return;
    }

    // Destroys the old input, if there is one
    this.destroy();

    // Get all properties which where at least one ref to view is set
    var props = shape
      .getStencil()
      .properties()
      .findAll(function (item) {
        return item.refToView() && item.refToView().length > 0 && item.directlyEditable();
      });
    // from these, get all properties where write access are and the type is String or Expression
    props = props.findAll(function (item) {
      return (
        !item.readonly() &&
        (item.type() == ORYX.CONFIG.TYPE_STRING || item.type() == ORYX.CONFIG.TYPE_EXPRESSION || item.type() == ORYX.CONFIG.TYPE_DATASOURCE)
      );
    });

    // Get all ref ids
    var allRefToViews = props
      .collect(function (prop) {
        return prop.refToView();
      })
      .flatten()
      .compact();
    // Get all labels from the shape with the ref ids
    var labels = shape.getLabels().findAll(function (label) {
      return allRefToViews.any(function (toView) {
        return label.id.endsWith(toView);
      });
    });

    // If there are no referenced labels --> return
    if (labels.length == 0) {
      return;
    }

    // Define the nearest label
    var nearestLabel = labels.length <= 1 ? labels[0] : null;
    if (!nearestLabel) {
      nearestLabel = labels.find(function (label) {
        return label.node == evt.target || label.node == evt.target.parentNode;
      });
      if (!nearestLabel) {
        var evtCoord = this.facade.eventCoordinates(evt);

        var additionalIEZoom = 1;
        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
          var ua = navigator.userAgent;
          if (ua.indexOf('MSIE') >= 0) {
            //IE 10 and below
            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
            if (zoom !== 100) {
              additionalIEZoom = zoom / 100;
            }
          }
        }

        if (additionalIEZoom !== 1) {
          evtCoord.x = evtCoord.x / additionalIEZoom;
          evtCoord.y = evtCoord.y / additionalIEZoom;
        }

        evtCoord.y += $('editor-header').clientHeight - $('canvasSection').scrollTop - 5;
        if (KISBPM.HEADER_CONFIG.showAppTitle == false) {
          evtCoord.y += 61;
        }

        evtCoord.x -= $('canvasSection').scrollLeft;

        var trans = this.facade.getCanvas().rootNode.lastChild.getScreenCTM();
        evtCoord.x *= trans.a;
        evtCoord.y *= trans.d;

        var diff = labels.collect(
          function (label) {
            var center = this.getCenterPosition(label.node);
            var len = Math.sqrt(Math.pow(center.x - evtCoord.x, 2) + Math.pow(center.y - evtCoord.y, 2));
            return { diff: len, label: label };
          }.bind(this)
        );

        diff.sort(function (a, b) {
          return a.diff > b.diff;
        });

        nearestLabel = diff[0].label;
      }
    }
    // Get the particular property for the label
    var prop = props.find(function (item) {
      return item.refToView().any(function (toView) {
        return nearestLabel.id == shape.id + toView;
      });
    });

    // Get the center position from the nearest label
    var width = Math.min(Math.max(100, shape.bounds.width()), 200);
    var center = this.getCenterPosition(nearestLabel.node, shape);
    center.x -= width / 2;
    var propId = prop.prefix() + '-' + prop.id();
    var textInput = document.createElement('textarea');
    textInput.id = 'shapeTextInput';
    textInput.style.position = 'absolute';
    textInput.style.width = width + 'px';
    textInput.style.left = center.x < 10 ? 10 : center.x + 'px';
    textInput.style.top = center.y - 15 + 'px';
    textInput.className = 'x-form-textarea x-form-field x_form_text_set_absolute';
    textInput.value = shape.properties[propId];
    this.oldValueText = shape.properties[propId];
    document.getElementById('canvasSection').appendChild(textInput);
    this.shownTextField = textInput;

    // Value change listener needs to be defined now since we reference it in the text field
    this.updateValueFunction = function (newValue, oldValue) {
      var currentEl = shape;
      var facade = this.facade;

      if (oldValue != newValue) {
        // Implement the specific command for property change
        var commandClass = ORYX.Core.Command.extend({
          construct: function () {
            this.el = currentEl;
            this.propId = propId;
            this.oldValue = oldValue;
            this.newValue = newValue;
            this.facade = facade;
          },
          execute: function () {
            this.el.setProperty(this.propId, this.newValue);
            //this.el.update();
            this.facade.setSelection([this.el]);
            this.facade.getCanvas().update();
            this.facade.updateSelection();
          },
          rollback: function () {
            this.el.setProperty(this.propId, this.oldValue);
            //this.el.update();
            this.facade.setSelection([this.el]);
            this.facade.getCanvas().update();
            this.facade.updateSelection();
          },
        });
        // Instantiated the class
        var command = new commandClass();

        // Execute the command
        this.facade.executeCommands([command]);
      }
    }.bind(this);

    jQuery('#shapeTextInput').focus();

    jQuery('#shapeTextInput').autogrow();

    // Disable the keydown in the editor (that when hitting the delete button, the shapes not get deleted)
    this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
  },

  getCenterPosition: function (svgNode, shape) {
    if (!svgNode) {
      return { x: 0, y: 0 };
    }

    var scale = this.facade.getCanvas().node.getScreenCTM();
    var absoluteXY = shape.bounds.upperLeft();

    var hasParent = true;
    var searchShape = shape;
    while (hasParent) {
      if (searchShape.getParentShape().getStencil().idWithoutNs() === 'BPMNDiagram') {
        hasParent = false;
      } else {
        var parentXY = searchShape.getParentShape().bounds.upperLeft();
        absoluteXY.x += parentXY.x;
        absoluteXY.y += parentXY.y;
        searchShape = searchShape.getParentShape();
      }
    }

    var center = shape.bounds.midPoint();
    center.x += absoluteXY.x + scale.e;
    center.y += absoluteXY.y + scale.f;

    center.x *= scale.a;
    center.y *= scale.d;

    var additionalIEZoom = 1;
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent;
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100;
        }
      }
    }

    if (additionalIEZoom === 1) {
      center.y = center.y - jQuery('#canvasSection').offset().top + 5;
      center.x -= jQuery('#canvasSection').offset().left;
    } else {
      var canvasOffsetLeft = jQuery('#canvasSection').offset().left;
      var canvasScrollLeft = jQuery('#canvasSection').scrollLeft();
      var canvasScrollTop = jQuery('#canvasSection').scrollTop();

      var offset = scale.e - canvasOffsetLeft * additionalIEZoom;
      var additionaloffset = 0;
      if (offset > 10) {
        additionaloffset = offset / additionalIEZoom - offset;
      }
      center.y =
        center.y - jQuery('#canvasSection').offset().top * additionalIEZoom + 5 + (canvasScrollTop * additionalIEZoom - canvasScrollTop);
      center.x =
        center.x - canvasOffsetLeft * additionalIEZoom + additionaloffset + (canvasScrollLeft * additionalIEZoom - canvasScrollLeft);
    }

    return center;
  },

  hide: function (e) {
    if (this.shownTextField && (!e || e.target !== this.shownTextField)) {
      var newValue = this.shownTextField.value;
      if (newValue !== this.oldValueText) {
        this.updateValueFunction(newValue, this.oldValueText);
      }
      this.destroy();
    }
  },

  hideField: function (e) {
    if (this.shownTextField) {
      this.destroy();
    }
  },

  destroy: function (e) {
    var textInputComp = jQuery('#shapeTextInput');
    if (textInputComp) {
      textInputComp.remove();
      delete this.shownTextField;

      this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
    }
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

/**
 * Supports EPCs by offering a syntax check and export and import ability..
 *
 *
 */
ORYX.Plugins.ProcessLink = Clazz.extend({
  facade: undefined,

  /**
   * Offers the plugin functionality:
   *
   */
  construct: function (facade) {
    this.facade = facade;

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPERTY_CHANGED, this.propertyChanged.bind(this));
  },

  /**
   *
   * @param {Object} option
   */
  propertyChanged: function (option, node) {
    if (option.name !== 'oryx-refuri' || !node instanceof ORYX.Core.Node) {
      return;
    }

    if (option.value && option.value.length > 0 && option.value != 'undefined') {
      this.show(node, option.value);
    } else {
      this.hide(node);
    }
  },

  /**
   * Shows the Link for a particular shape with a specific url
   *
   * @param {Object} shape
   * @param {Object} url
   */
  show: function (shape, url) {
    // Generate the svg-representation of a link
    var link = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'a',
      { target: '_blank' },
      [
        'path',
        {
          'stroke-width': 1.0,
          stroke: '#00DD00',
          fill: '#00AA00',
          d: 'M3,3 l0,-2.5 l7.5,0 l0,-2.5 l7.5,4.5 l-7.5,3.5 l0,-2.5 l-8,0',
          'line-captions': 'round',
        },
      ],
    ]);

    var link = ORYX.Editor.graft('http://www.w3.org/2000/svg', null, [
      'a',
      { target: '_blank' },
      [
        'path',
        {
          style: 'fill:#92BFFC;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72',
          d: 'M0 1.44 L0 15.05 L11.91 15.05 L11.91 5.98 L7.37 1.44 L0 1.44 Z',
        },
      ],
      [
        'path',
        {
          style: 'stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72;fill:none;',
          transform: 'translate(7.5, -8.5)',
          d: 'M0 10.51 L0 15.05 L4.54 15.05',
        },
      ],
      [
        'path',
        {
          style: 'fill:#f28226;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72',
          transform: 'translate(-3, -1)',
          d: 'M0 8.81 L0 13.06 L5.95 13.06 L5.95 15.05 A50.2313 50.2313 -175.57 0 0 10.77 11.08 A49.9128 49.9128 -1.28 0 0 5.95 6.54 L5.95 8.81 L0 8.81 Z',
        },
      ],
    ]);

    /*
	 * 
	 * 					[ 'a',
						{'target': '_blank'},
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M7,4 l0,2"}],
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M4,8 l-2,0 l0,6"}],
						['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M10,8 l2,0 l0,6"}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 0}],
						['rect', { "style": "fill:#ffafff;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 6}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 0, "y": 12}],
						['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 8, "y": 12}],
						['rect', { "style": "fill:none;stroke:none;pointer-events:all", "width": 14, "height": 16, "x": 0, "y": 0}]
					]);
	 */

    // Set the link with the special namespace
    link.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url);

    // Shows the link in the overlay
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_SHOW,
      id: 'arissupport.urlref_' + shape.id,
      shapes: [shape],
      node: link,
      nodePosition: 'SE',
    });
  },

  /**
   * Hides the Link for a particular shape
   *
   * @param {Object} shape
   */
  hide: function (shape) {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_OVERLAY_HIDE,
      id: 'arissupport.urlref_' + shape.id,
    });
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

Array.prototype.insertFrom = function (from, to) {
  to = Math.max(0, to);
  from = Math.min(Math.max(0, from), this.length - 1);

  var el = this[from];
  var old = this.without(el);
  var newA = old.slice(0, to);
  newA.push(el);
  if (old.length > to) {
    newA = newA.concat(old.slice(to));
  }
  return newA;
};

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.Arrangement = ORYX.Plugins.AbstractPlugin.extend({
  facade: undefined,

  construct: function (facade) {
    this.facade = facade;

    // Z-Ordering
    /** Hide for SIGNAVIO 
		
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.btf,
			'functionality': this.setZLevel.bind(this, this.setToTop),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_front.png",
			'description': ORYX.I18N.Arrangement.btfDesc,
			'index': 1,
			'minShape': 1});
			
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.btb,
			'functionality': this.setZLevel.bind(this, this.setToBack),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_back.png",
			'description': ORYX.I18N.Arrangement.btbDesc,
			'index': 2,
			'minShape': 1});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.bf,
			'functionality': this.setZLevel.bind(this, this.setForward),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_forwards.png",
			'description': ORYX.I18N.Arrangement.bfDesc,
			'index': 3,
			'minShape': 1});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.bb,
			'functionality': this.setZLevel.bind(this, this.setBackward),
			'group': ORYX.I18N.Arrangement.groupZ,
			'icon': ORYX.PATH + "images/shape_move_backwards.png",
			'description': ORYX.I18N.Arrangement.bbDesc,
			'index': 4,
			'minShape': 1});

		// Aligment
		this.facade.offer({
			'name':ORYX.I18N.Arrangement.ab,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_BOTTOM]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_bottom.png",
			'description': ORYX.I18N.Arrangement.abDesc,
			'index': 1,
			'minShape': 2});



		this.facade.offer({
			'name':ORYX.I18N.Arrangement.at,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_TOP]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_top.png",
			'description': ORYX.I18N.Arrangement.atDesc,
			'index': 3,
			'minShape': 2});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.al,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_LEFT]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_left.png",
			'description': ORYX.I18N.Arrangement.alDesc,
			'index': 4,
			'minShape': 2});

		this.facade.offer({
			'name':ORYX.I18N.Arrangement.ar,
			'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_RIGHT]),
			'group': ORYX.I18N.Arrangement.groupA,
			'icon': ORYX.PATH + "images/shape_align_right.png",
			'description': ORYX.I18N.Arrangement.arDesc,
			'index': 6,
			'minShape': 2});

		**/

    this.facade.offer({
      name: ORYX.I18N.Arrangement.am,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_MIDDLE]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_middle.png',
      description: ORYX.I18N.Arrangement.amDesc,
      index: 1,
      minShape: 2,
    });

    this.facade.offer({
      name: ORYX.I18N.Arrangement.ac,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_CENTER]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_center.png',
      description: ORYX.I18N.Arrangement.acDesc,
      index: 2,
      minShape: 2,
    });

    this.facade.offer({
      name: ORYX.I18N.Arrangement.as,
      functionality: this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_SIZE]),
      group: ORYX.I18N.Arrangement.groupA,
      icon: ORYX.PATH + 'images/shape_align_size.png',
      description: ORYX.I18N.Arrangement.asDesc,
      index: 3,
      minShape: 2,
    });

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, this.setZLevel.bind(this, this.setToTop));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACK, this.setZLevel.bind(this, this.setToBack));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD, this.setZLevel.bind(this, this.setForward));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD, this.setZLevel.bind(this, this.setBackward));
  },

  onSelectionChanged: function (elemnt) {
    var selection = this.facade.getSelection();
    if (selection.length === 1 && selection[0] instanceof ORYX.Core.Edge) {
      this.setToTop(selection);
    }
  },

  setZLevel: function (callback, event) {
    //Command-Pattern for dragging one docker
    var zLevelCommand = ORYX.Core.Command.extend({
      construct: function (callback, elements, facade) {
        this.callback = callback;
        this.elements = elements;
        // For redo, the previous elements get stored
        this.elAndIndex = elements.map(function (el) {
          return { el: el, previous: el.parent.children[el.parent.children.indexOf(el) - 1] };
        });
        this.facade = facade;
      },
      execute: function () {
        // Call the defined z-order callback with the elements
        this.callback(this.elements);
        this.facade.setSelection(this.elements);
      },
      rollback: function () {
        // Sort all elements on the index of there containment
        var sortedEl = this.elAndIndex.sortBy(function (el) {
          var value = el.el;
          var t = $A(value.node.parentNode.childNodes);
          return t.indexOf(value.node);
        });

        // Every element get setted back bevor the old previous element
        for (var i = 0; i < sortedEl.length; i++) {
          var el = sortedEl[i].el;
          var p = el.parent;
          var oldIndex = p.children.indexOf(el);
          var newIndex = p.children.indexOf(sortedEl[i].previous);
          newIndex = newIndex || 0;
          p.children = p.children.insertFrom(oldIndex, newIndex);
          el.node.parentNode.insertBefore(el.node, el.node.parentNode.childNodes[newIndex + 1]);
        }

        // Reset the selection
        this.facade.setSelection(this.elements);
      },
    });

    // Instanziate the dockCommand
    var command = new zLevelCommand(callback, this.facade.getSelection(), this.facade);
    if (event.excludeCommand) {
      command.execute();
    } else {
      this.facade.executeCommands([command]);
    }
  },

  setToTop: function (elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function (value, index) {
      var t = $A(value.node.parentNode.childNodes);
      return t.indexOf(value.node);
    });
    // Sortiertes Array wird nach oben verschoben.
    tmpElem.each(function (value) {
      var p = value.parent;
      if (p.children.last() === value) {
        return;
      }
      p.children = p.children.without(value);
      p.children.push(value);
      value.node.parentNode.appendChild(value.node);
    });
  },

  setToBack: function (elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function (value, index) {
      var t = $A(value.node.parentNode.childNodes);
      return t.indexOf(value.node);
    });

    tmpElem = tmpElem.reverse();

    // Sortiertes Array wird nach unten verschoben.
    tmpElem.each(function (value) {
      var p = value.parent;
      p.children = p.children.without(value);
      p.children.unshift(value);
      value.node.parentNode.insertBefore(value.node, value.node.parentNode.firstChild);
    });
  },

  setBackward: function (elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function (value, index) {
      var t = $A(value.node.parentNode.childNodes);
      return t.indexOf(value.node);
    });

    // Reverse the elements
    tmpElem = tmpElem.reverse();

    // Delete all Nodes who are the next Node in the nodes-Array
    var compactElem = tmpElem.findAll(function (el) {
      return !tmpElem.some(function (checkedEl) {
        return checkedEl.node == el.node.previousSibling;
      });
    });

    // Sortiertes Array wird nach eine Ebene nach oben verschoben.
    compactElem.each(function (el) {
      if (el.node.previousSibling === null) {
        return;
      }
      var p = el.parent;
      var index = p.children.indexOf(el);
      p.children = p.children.insertFrom(index, index - 1);
      el.node.parentNode.insertBefore(el.node, el.node.previousSibling);
    });
  },

  setForward: function (elements) {
    // Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
    var tmpElem = elements.sortBy(function (value, index) {
      var t = $A(value.node.parentNode.childNodes);
      return t.indexOf(value.node);
    });

    // Delete all Nodes who are the next Node in the nodes-Array
    var compactElem = tmpElem.findAll(function (el) {
      return !tmpElem.some(function (checkedEl) {
        return checkedEl.node == el.node.nextSibling;
      });
    });

    // Sortiertes Array wird eine Ebene nach unten verschoben.
    compactElem.each(function (el) {
      var nextNode = el.node.nextSibling;
      if (nextNode === null) {
        return;
      }
      var index = el.parent.children.indexOf(el);
      var p = el.parent;
      p.children = p.children.insertFrom(index, index + 1);
      el.node.parentNode.insertBefore(nextNode, el.node);
    });
  },

  alignShapes: function (way) {
    var elements = this.facade.getSelection();

    // Set the elements to all Top-Level elements
    elements = this.facade.getCanvas().getShapesWithSharedParent(elements);
    // Get only nodes
    elements = elements.findAll(function (value) {
      return value instanceof ORYX.Core.Node;
    });
    // Delete all attached intermediate events from the array
    elements = elements.findAll(function (value) {
      var d = value.getIncomingShapes();
      return d.length == 0 || !elements.include(d[0]);
    });
    if (elements.length < 2) {
      return;
    }

    // get bounds of all shapes.
    var bounds = elements[0].absoluteBounds().clone();
    elements.each(function (shape) {
      bounds.include(shape.absoluteBounds().clone());
    });

    // get biggest width and heigth
    var maxWidth = 0;
    var maxHeight = 0;
    elements.each(function (shape) {
      maxWidth = Math.max(shape.bounds.width(), maxWidth);
      maxHeight = Math.max(shape.bounds.height(), maxHeight);
    });

    var commandClass = ORYX.Core.Command.extend({
      construct: function (elements, bounds, maxHeight, maxWidth, way, plugin) {
        this.elements = elements;
        this.bounds = bounds;
        this.maxHeight = maxHeight;
        this.maxWidth = maxWidth;
        this.way = way;
        this.facade = plugin.facade;
        this.plugin = plugin;
        this.orgPos = [];
      },
      setBounds: function (shape, maxSize) {
        if (!maxSize) maxSize = { width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE };

        if (!shape.bounds) {
          throw 'Bounds not definined.';
        }

        var newBounds = {
          a: {
            x: shape.bounds.upperLeft().x - (this.maxWidth - shape.bounds.width()) / 2,
            y: shape.bounds.upperLeft().y - (this.maxHeight - shape.bounds.height()) / 2,
          },
          b: {
            x: shape.bounds.lowerRight().x + (this.maxWidth - shape.bounds.width()) / 2,
            y: shape.bounds.lowerRight().y + (this.maxHeight - shape.bounds.height()) / 2,
          },
        };

        /* If the new width of shape exceeds the maximum width, set width value to maximum. */
        if (this.maxWidth > maxSize.width) {
          newBounds.a.x = shape.bounds.upperLeft().x - (maxSize.width - shape.bounds.width()) / 2;

          newBounds.b.x = shape.bounds.lowerRight().x + (maxSize.width - shape.bounds.width()) / 2;
        }

        /* If the new height of shape exceeds the maximum height, set height value to maximum. */
        if (this.maxHeight > maxSize.height) {
          newBounds.a.y = shape.bounds.upperLeft().y - (maxSize.height - shape.bounds.height()) / 2;

          newBounds.b.y = shape.bounds.lowerRight().y + (maxSize.height - shape.bounds.height()) / 2;
        }

        /* set bounds of shape */
        shape.bounds.set(newBounds);
      },
      execute: function () {
        // align each shape according to the way that was specified.
        this.elements.each(
          function (shape, index) {
            this.orgPos[index] = shape.bounds.upperLeft();

            var relBounds = this.bounds.clone();
            var newCoordinates;
            if (shape.parent && !(shape.parent instanceof ORYX.Core.Canvas)) {
              var upL = shape.parent.absoluteBounds().upperLeft();
              relBounds.moveBy(-upL.x, -upL.y);
            }

            switch (this.way) {
              // align the shapes in the requested way.
              case ORYX.CONFIG.EDITOR_ALIGN_BOTTOM:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: relBounds.b.y - shape.bounds.height(),
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_MIDDLE:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: (relBounds.a.y + relBounds.b.y - shape.bounds.height()) / 2,
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_TOP:
                newCoordinates = {
                  x: shape.bounds.upperLeft().x,
                  y: relBounds.a.y,
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_LEFT:
                newCoordinates = {
                  x: relBounds.a.x,
                  y: shape.bounds.upperLeft().y,
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_CENTER:
                newCoordinates = {
                  x: (relBounds.a.x + relBounds.b.x - shape.bounds.width()) / 2,
                  y: shape.bounds.upperLeft().y,
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_RIGHT:
                newCoordinates = {
                  x: relBounds.b.x - shape.bounds.width(),
                  y: shape.bounds.upperLeft().y,
                };
                break;

              case ORYX.CONFIG.EDITOR_ALIGN_SIZE:
                if (shape.isResizable) {
                  this.orgPos[index] = { a: shape.bounds.upperLeft(), b: shape.bounds.lowerRight() };
                  this.setBounds(shape, shape.maximumSize);
                }
                break;
            }

            if (newCoordinates) {
              var offset = {
                x: shape.bounds.upperLeft().x - newCoordinates.x,
                y: shape.bounds.upperLeft().y - newCoordinates.y,
              };
              // Set the new position
              shape.bounds.moveTo(newCoordinates);
              this.plugin.layoutEdges(shape, shape.getAllDockedShapes(), offset);
              //shape.update()
            }
          }.bind(this)
        );

        //this.facade.getCanvas().update();
        //this.facade.updateSelection();
      },
      rollback: function () {
        this.elements.each(
          function (shape, index) {
            if (this.way == ORYX.CONFIG.EDITOR_ALIGN_SIZE) {
              if (shape.isResizable) {
                shape.bounds.set(this.orgPos[index]);
              }
            } else {
              shape.bounds.moveTo(this.orgPos[index]);
            }
          }.bind(this)
        );

        //this.facade.getCanvas().update();
        //this.facade.updateSelection();
      },
    });

    var command = new commandClass(elements, bounds, maxHeight, maxWidth, parseInt(way), this);

    this.facade.executeCommands([command]);
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.Save = Clazz.extend({
  facade: undefined,

  processURI: undefined,

  changeSymbol: '*',

  construct: function (facade) {
    this.facade = facade;

    document.addEventListener(
      'keydown',
      function (e) {
        if (e.ctrlKey && e.keyCode === 83) {
          Event.stop(e);
        }
      },
      false
    );

    window.onbeforeunload = this.onUnLoad.bind(this);

    this.changeDifference = 0;

    // Register on event for executing commands --> store all commands in a stack
    // --> Execute
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_UNDO_EXECUTE,
      function () {
        this.changeDifference++;
        this.updateTitle();
      }.bind(this)
    );
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_EXECUTE_COMMANDS,
      function () {
        this.changeDifference++;
        this.updateTitle();
      }.bind(this)
    );
    // --> Saved from other places in the editor
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_SAVED,
      function () {
        this.changeDifference = 0;
        this.updateTitle();
      }.bind(this)
    );

    // --> Rollback
    this.facade.registerOnEvent(
      ORYX.CONFIG.EVENT_UNDO_ROLLBACK,
      function () {
        this.changeDifference--;
        this.updateTitle();
      }.bind(this)
    );

    //TODO very critical for load time performance!!!
    //this.serializedDOM = DataManager.__persistDOM(this.facade);

    this.hasChanges = this._hasChanges.bind(this);
  },

  updateTitle: function () {
    var value = window.document.title || document.getElementsByTagName('title')[0].childNodes[0].nodeValue;

    if (this.changeDifference === 0 && value.startsWith(this.changeSymbol)) {
      window.document.title = value.slice(1);
    } else if (this.changeDifference !== 0 && !value.startsWith(this.changeSymbol)) {
      window.document.title = this.changeSymbol + '' + value;
    }
  },

  _hasChanges: function () {
    return this.changeDifference !== 0 || (this.facade.getModelMetaData()['new'] && this.facade.getCanvas().getChildShapes().size() > 0);
  },

  onUnLoad: function () {
    if (this._hasChanges()) {
      return ORYX.I18N.Save.unsavedData;
    }
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.DragDropResize = ORYX.Plugins.AbstractPlugin.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function (facade) {
    this.facade = facade;

    // Initialize variables
    this.currentShapes = []; // Current selected Shapes
    //this.pluginsData 		= [];			// Available Plugins
    this.toMoveShapes = []; // Shapes there will be moved
    this.distPoints = []; // Distance Points for Snap on Grid
    this.isResizing = false; // Flag: If there was currently resized
    this.dragEnable = false; // Flag: If Dragging is enabled
    this.dragIntialized = false; // Flag: If the Dragging is initialized
    this.edgesMovable = true; // Flag: If an edge is docked it is not movable
    this.offSetPosition = { x: 0, y: 0 }; // Offset of the Dragging
    this.faktorXY = { x: 1, y: 1 }; // The Current Zoom-Faktor
    this.containmentParentNode; // the current future parent node for the dragged shapes
    this.isAddingAllowed = false; // flag, if adding current selected shapes to containmentParentNode is allowed
    this.isAttachingAllowed = false; // flag, if attaching to the current shape is allowed

    this.callbackMouseMove = this.handleMouseMove.bind(this);
    this.callbackMouseUp = this.handleMouseUp.bind(this);

    // Get the SVG-Containernode
    var containerNode = this.facade.getCanvas().getSvgContainer();

    // Create the Selected Rectangle in the SVG
    this.selectedRect = new ORYX.Plugins.SelectedRect(containerNode);

    // Show grid line if enabled
    if (ORYX.CONFIG.SHOW_GRIDLINE) {
      this.vLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_VERTICAL);
      this.hLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_HORIZONTAL);
    }

    // Get a HTML-ContainerNode
    containerNode = this.facade.getCanvas().getHTMLContainer();

    this.scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode;

    // Create the southeastern button for resizing
    this.resizerSE = new ORYX.Plugins.Resizer(containerNode, 'southeast', this.facade);
    this.resizerSE.registerOnResize(this.onResize.bind(this)); // register the resize callback
    this.resizerSE.registerOnResizeEnd(this.onResizeEnd.bind(this)); // register the resize end callback
    this.resizerSE.registerOnResizeStart(this.onResizeStart.bind(this)); // register the resize start callback

    // Create the northwestern button for resizing
    this.resizerNW = new ORYX.Plugins.Resizer(containerNode, 'northwest', this.facade);
    this.resizerNW.registerOnResize(this.onResize.bind(this)); // register the resize callback
    this.resizerNW.registerOnResizeEnd(this.onResizeEnd.bind(this)); // register the resize end callback
    this.resizerNW.registerOnResizeStart(this.onResizeStart.bind(this)); // register the resize start callback

    // For the Drag and Drop
    // Register on MouseDown-Event on a Shape
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
  },

  /**
   * On Mouse Down
   *
   */
  handleMouseDown: function (event, uiObj) {
    // If the selection Bounds not intialized and the uiObj is not member of current selectio
    // then return
    if (!this.dragBounds || !this.currentShapes.member(uiObj) || !this.toMoveShapes.length) {
      return;
    }

    // Start Dragging
    this.dragEnable = true;
    this.dragIntialized = true;
    this.edgesMovable = true;

    // Calculate the current zoom factor
    var a = this.facade.getCanvas().node.getScreenCTM();
    this.faktorXY.x = a.a;
    this.faktorXY.y = a.d;

    var eventX = Event.pointerX(event);
    var eventY = Event.pointerY(event);

    // Set the offset position of dragging
    var upL = this.dragBounds.upperLeft();
    this.offSetPosition = {
      x: eventX - upL.x * this.faktorXY.x,
      y: eventY - upL.y * this.faktorXY.y,
    };

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop };

    // Register on Global Mouse-MOVE Event
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false);
    // Register on Global Mouse-UP Event
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true);

    return;
  },

  /**
   * On Key Mouse Up
   *
   */
  handleMouseUp: function (event) {
    //disable containment highlighting
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: 'dragdropresize.contain',
    });

    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
      highlightId: 'dragdropresize.attached',
    });

    // If Dragging is finished
    if (this.dragEnable) {
      // and update the current selection
      if (!this.dragIntialized) {
        // Do Method after Dragging
        this.afterDrag();

        // Check if the Shape is allowed to dock to the other Shape
        if (
          this.isAttachingAllowed &&
          this.toMoveShapes.length == 1 &&
          this.toMoveShapes[0] instanceof ORYX.Core.Node &&
          this.toMoveShapes[0].dockers.length > 0
        ) {
          // Get the position and the docker
          var position = this.facade.eventCoordinates(event);
          var docker = this.toMoveShapes[0].dockers[0];

          //Command-Pattern for dragging several Shapes
          var dockCommand = ORYX.Core.Command.extend({
            construct: function (docker, position, newDockedShape, facade) {
              this.docker = docker;
              this.newPosition = position;
              this.newDockedShape = newDockedShape;
              this.newParent = newDockedShape.parent || facade.getCanvas();
              this.oldPosition = docker.parent.bounds.center();
              this.oldDockedShape = docker.getDockedShape();
              this.oldParent = docker.parent.parent || facade.getCanvas();
              this.facade = facade;

              if (this.oldDockedShape) {
                this.oldPosition = docker.parent.absoluteBounds().center();
              }
            },
            execute: function () {
              this.dock(this.newDockedShape, this.newParent, this.newPosition);

              // Raise Event for having the docked shape on top of the other shape
              this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, excludeCommand: true });
            },
            rollback: function () {
              this.dock(this.oldDockedShape, this.oldParent, this.oldPosition);
            },
            dock: function (toDockShape, parent, pos) {
              // Add to the same parent Shape
              parent.add(this.docker.parent);

              // Set the Docker to the new Shape
              this.docker.setDockedShape(undefined);
              this.docker.bounds.centerMoveTo(pos);
              this.docker.setDockedShape(toDockShape);
              //this.docker.update();

              this.facade.setSelection([this.docker.parent]);
              this.facade.getCanvas().update();
              this.facade.updateSelection();
            },
          });

          // Instanziate the dockCommand
          var commands = [new dockCommand(docker, position, this.containmentParentNode, this.facade)];
          this.facade.executeCommands(commands);

          // Check if adding is allowed to the other Shape
        } else if (this.isAddingAllowed) {
          // Refresh all Shapes --> Set the new Bounds
          this.refreshSelectedShapes();
        }

        this.facade.updateSelection();

        //this.currentShapes.each(function(shape) {shape.update()})
        // Raise Event: Dragging is finished
        this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_DRAGDROP_END });
      }

      if (this.vLine) this.vLine.hide();
      if (this.hLine) this.hLine.hide();
    }

    // Disable
    this.dragEnable = false;

    // UnRegister on Global Mouse-UP/-Move Event
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true);
    document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false);

    return;
  },

  /**
   * On Key Mouse Move
   *
   */
  handleMouseMove: function (event) {
    // If dragging is not enabled, go return
    if (!this.dragEnable) {
      return;
    }
    // If Dragging is initialized
    if (this.dragIntialized) {
      // Raise Event: Drag will be started
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_DRAGDROP_START });
      this.dragIntialized = false;

      // And hide the resizers and the highlighting
      this.resizerSE.hide();
      this.resizerNW.hide();

      // if only edges are selected, containmentParentNode must be the canvas
      this._onlyEdges = this.currentShapes.all(function (currentShape) {
        return currentShape instanceof ORYX.Core.Edge;
      });

      // Do method before Drag
      this.beforeDrag();

      this._currentUnderlyingNodes = [];
    }

    // Calculate the new position
    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y,
    };

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft;
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop;

    // If not the Control-Key are pressed
    var modifierKeyPressed = event.shiftKey || event.ctrlKey;
    if (ORYX.CONFIG.GRID_ENABLED && !modifierKeyPressed) {
      // Snap the current position to the nearest Snap-Point
      position = this.snapToGrid(position);
    } else {
      if (this.vLine) this.vLine.hide();
      if (this.hLine) this.hLine.hide();
    }

    // Adjust the point by the zoom faktor
    position.x /= this.faktorXY.x;
    position.y /= this.faktorXY.y;

    // Set that the position is not lower than zero
    position.x = Math.max(0, position.x);
    position.y = Math.max(0, position.y);

    // Set that the position is not bigger than the canvas
    var c = this.facade.getCanvas();
    position.x = Math.min(c.bounds.width() - this.dragBounds.width(), position.x);
    position.y = Math.min(c.bounds.height() - this.dragBounds.height(), position.y);

    // Drag this bounds
    this.dragBounds.moveTo(position);

    // Update all selected shapes and the selection rectangle
    //this.refreshSelectedShapes();
    this.resizeRectangle(this.dragBounds);

    this.isAttachingAllowed = false;

    //check, if a node can be added to the underlying node
    var eventCoordinates = this.facade.eventCoordinates(event);

    var additionalIEZoom = 1;
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent;
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100;
        }
      }
    }

    if (additionalIEZoom !== 1) {
      eventCoordinates.x = eventCoordinates.x / additionalIEZoom;
      eventCoordinates.y = eventCoordinates.y / additionalIEZoom;
    }

    var underlyingNodes = $A(this.facade.getCanvas().getAbstractShapesAtPosition(eventCoordinates));

    var checkIfAttachable =
      this.toMoveShapes.length == 1 && this.toMoveShapes[0] instanceof ORYX.Core.Node && this.toMoveShapes[0].dockers.length > 0;
    checkIfAttachable = checkIfAttachable && underlyingNodes.length != 1;

    if (
      !checkIfAttachable &&
      underlyingNodes.length === this._currentUnderlyingNodes.length &&
      underlyingNodes.all(
        function (node, index) {
          return this._currentUnderlyingNodes[index] === node;
        }.bind(this)
      )
    ) {
      return;
    } else if (this._onlyEdges) {
      this.isAddingAllowed = true;
      this.containmentParentNode = this.facade.getCanvas();
    } else {
      /* Check the containment and connection rules */
      var options = {
        event: event,
        underlyingNodes: underlyingNodes,
        checkIfAttachable: checkIfAttachable,
      };
      this.checkRules(options);
    }

    this._currentUnderlyingNodes = underlyingNodes.reverse();

    //visualize the containment result
    if (this.isAttachingAllowed) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
        highlightId: 'dragdropresize.attached',
        elements: [this.containmentParentNode],
        style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
        color: ORYX.CONFIG.SELECTION_VALID_COLOR,
      });
    } else {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
        highlightId: 'dragdropresize.attached',
      });
    }

    if (!this.isAttachingAllowed) {
      if (this.isAddingAllowed) {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'dragdropresize.contain',
          elements: [this.containmentParentNode],
          color: ORYX.CONFIG.SELECTION_VALID_COLOR,
        });
      } else {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'dragdropresize.contain',
          elements: [this.containmentParentNode],
          color: ORYX.CONFIG.SELECTION_INVALID_COLOR,
        });
      }
    } else {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
        highlightId: 'dragdropresize.contain',
      });
    }

    // Stop the Event
    //Event.stop(event);
    return;
  },

  //	/**
  //	 * Rollbacks the docked shape of an edge, if the edge is not movable.
  //	 */
  //	redockEdges: function() {
  //		this._undockedEdgesCommand.dockers.each(function(el){
  //			el.docker.setDockedShape(el.dockedShape);
  //			el.docker.setReferencePoint(el.refPoint);
  //		})
  //	},

  /**
   *  Checks the containment and connection rules for the selected shapes.
   */
  checkRules: function (options) {
    var event = options.event;
    var underlyingNodes = options.underlyingNodes;
    var checkIfAttachable = options.checkIfAttachable;
    var noEdges = options.noEdges;

    //get underlying node that is not the same than one of the currently selected shapes or
    // a child of one of the selected shapes with the highest z Order.
    // The result is a shape or the canvas
    this.containmentParentNode = underlyingNodes.reverse().find(
      function (node) {
        return (
          node instanceof ORYX.Core.Canvas ||
          ((node instanceof ORYX.Core.Node || (node instanceof ORYX.Core.Edge && !noEdges)) &&
            !(
              this.currentShapes.member(node) ||
              this.currentShapes.any(function (shape) {
                return shape.children.length > 0 && shape.getChildNodes(true).member(node);
              })
            ))
        );
      }.bind(this)
    );

    if (checkIfAttachable) {
      this.isAttachingAllowed = this.facade.getRules().canConnect({
        sourceShape: this.containmentParentNode,
        edgeShape: this.toMoveShapes[0],
        targetShape: this.toMoveShapes[0],
      });

      if (this.isAttachingAllowed) {
        var point = this.facade.eventCoordinates(event);
        this.isAttachingAllowed = this.containmentParentNode.isPointOverOffset(point.x, point.y);
      }
    }

    if (!this.isAttachingAllowed) {
      //check all selected shapes, if they can be added to containmentParentNode
      this.isAddingAllowed = this.toMoveShapes.all(
        function (currentShape) {
          if (
            currentShape instanceof ORYX.Core.Edge ||
            currentShape instanceof ORYX.Core.Controls.Docker ||
            this.containmentParentNode === currentShape.parent
          ) {
            return true;
          } else if (this.containmentParentNode !== currentShape) {
            if (!(this.containmentParentNode instanceof ORYX.Core.Edge) || !noEdges) {
              if (this.facade.getRules().canContain({ containingShape: this.containmentParentNode, containedShape: currentShape })) {
                return true;
              }
            }
          }
          return false;
        }.bind(this)
      );
    }

    if (!this.isAttachingAllowed && !this.isAddingAllowed && this.containmentParentNode instanceof ORYX.Core.Edge) {
      options.noEdges = true;
      options.underlyingNodes.reverse();
      this.checkRules(options);
    }
  },

  /**
   * Redraw the selected Shapes.
   *
   */
  refreshSelectedShapes: function () {
    // If the selection bounds not initialized, return
    if (!this.dragBounds) {
      return;
    }

    // Calculate the offset between the bounds and the old bounds
    var upL = this.dragBounds.upperLeft();
    var oldUpL = this.oldDragBounds.upperLeft();
    var offset = {
      x: upL.x - oldUpL.x,
      y: upL.y - oldUpL.y,
    };

    // Instanciate the dragCommand
    var commands = [new ORYX.Core.Command.Move(this.toMoveShapes, offset, this.containmentParentNode, this.currentShapes, this)];
    // If the undocked edges command is setted, add this command
    if (this._undockedEdgesCommand instanceof ORYX.Core.Command) {
      commands.unshift(this._undockedEdgesCommand);
    }
    // Execute the commands
    this.facade.executeCommands(commands);

    // copy the bounds to the old bounds
    if (this.dragBounds) this.oldDragBounds = this.dragBounds.clone();
  },

  /**
   * Callback for Resize
   *
   */
  onResize: function (bounds) {
    // If the selection bounds not initialized, return
    if (!this.dragBounds) {
      return;
    }

    this.dragBounds = bounds;
    this.isResizing = true;

    // Update the rectangle
    this.resizeRectangle(this.dragBounds);
  },

  onResizeStart: function () {
    this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_RESIZE_START });
  },

  onResizeEnd: function () {
    if (!(this.currentShapes instanceof Array) || this.currentShapes.length <= 0) {
      return;
    }

    // If Resizing finished, the Shapes will be resize
    if (this.isResizing) {
      var commandClass = ORYX.Core.Command.extend({
        construct: function (shape, newBounds, plugin) {
          this.shape = shape;
          this.oldBounds = shape.bounds.clone();
          this.newBounds = newBounds;
          this.plugin = plugin;
        },
        execute: function () {
          this.shape.bounds.set(this.newBounds.a, this.newBounds.b);
          this.update(this.getOffset(this.oldBounds, this.newBounds));
        },
        rollback: function () {
          this.shape.bounds.set(this.oldBounds.a, this.oldBounds.b);
          this.update(this.getOffset(this.newBounds, this.oldBounds));
        },

        getOffset: function (b1, b2) {
          return {
            x: b2.a.x - b1.a.x,
            y: b2.a.y - b1.a.y,
            xs: b2.width() / b1.width(),
            ys: b2.height() / b1.height(),
          };
        },
        update: function (offset) {
          this.shape.getLabels().each(function (label) {
            label.changed();
          });

          var allEdges = []
            .concat(this.shape.getIncomingShapes())
            .concat(this.shape.getOutgoingShapes())
            // Remove all edges which are included in the selection from the list
            .findAll(
              function (r) {
                return r instanceof ORYX.Core.Edge;
              }.bind(this)
            );

          this.plugin.layoutEdges(this.shape, allEdges, offset);

          this.plugin.facade.setSelection([this.shape]);
          this.plugin.facade.getCanvas().update();
          this.plugin.facade.updateSelection();
        },
      });

      var bounds = this.dragBounds.clone();
      var shape = this.currentShapes[0];

      if (shape.parent) {
        var parentPosition = shape.parent.absoluteXY();
        bounds.moveBy(-parentPosition.x, -parentPosition.y);
      }

      var command = new commandClass(shape, bounds, this);

      this.facade.executeCommands([command]);

      this.isResizing = false;

      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_RESIZE_END });
    }
  },

  /**
   * Prepare the Dragging
   *
   */
  beforeDrag: function () {
    var undockEdgeCommand = ORYX.Core.Command.extend({
      construct: function (moveShapes) {
        this.dockers = moveShapes
          .collect(function (shape) {
            return shape instanceof ORYX.Core.Controls.Docker
              ? { docker: shape, dockedShape: shape.getDockedShape(), refPoint: shape.referencePoint }
              : undefined;
          })
          .compact();
      },
      execute: function () {
        this.dockers.each(function (el) {
          el.docker.setDockedShape(undefined);
        });
      },
      rollback: function () {
        this.dockers.each(function (el) {
          el.docker.setDockedShape(el.dockedShape);
          el.docker.setReferencePoint(el.refPoint);
          //el.docker.update();
        });
      },
    });

    this._undockedEdgesCommand = new undockEdgeCommand(this.toMoveShapes);
    this._undockedEdgesCommand.execute();
  },

  hideAllLabels: function (shape) {
    // Hide all labels from the shape
    shape.getLabels().each(function (label) {
      label.hide();
    });
    // Hide all labels from docked shapes
    shape.getAllDockedShapes().each(function (dockedShape) {
      var labels = dockedShape.getLabels();
      if (labels.length > 0) {
        labels.each(function (label) {
          label.hide();
        });
      }
    });

    // Do this recursive for all child shapes
    // EXP-NICO use getShapes
    shape.getChildren().each(
      function (value) {
        if (value instanceof ORYX.Core.Shape) this.hideAllLabels(value);
      }.bind(this)
    );
  },

  /**
   * Finished the Dragging
   *
   */
  afterDrag: function () {},

  /**
   * Show all Labels at these shape
   *
   */
  showAllLabels: function (shape) {
    // Show the label of these shape
    //shape.getLabels().each(function(label) {
    for (var i = 0; i < shape.length; i++) {
      var label = shape[i];
      label.show();
    } //);
    // Show all labels at docked shapes
    //shape.getAllDockedShapes().each(function(dockedShape) {
    var allDockedShapes = shape.getAllDockedShapes();
    for (var i = 0; i < allDockedShapes.length; i++) {
      var dockedShape = allDockedShapes[i];
      var labels = dockedShape.getLabels();
      if (labels.length > 0) {
        labels.each(function (label) {
          label.show();
        });
      }
    } //);

    // Do this recursive
    //shape.children.each((function(value) {
    for (var i = 0; i < shape.children.length; i++) {
      var value = shape.children[i];
      if (value instanceof ORYX.Core.Shape) this.showAllLabels(value);
    } //).bind(this));
  },

  /**
   * Intialize Method, if there are new Plugins
   *
   */
  /*registryChanged: function(pluginsData) {
		// Save all new Plugin, sorted by group and index
		this.pluginsData = pluginsData.sortBy( function(value) {
			return (value.group + "" + value.index);
		});
	},*/

  /**
   * On the Selection-Changed
   *
   */
  onSelectionChanged: function (event) {
    var elements = event.elements;

    // Reset the drag-variables
    this.dragEnable = false;
    this.dragIntialized = false;
    this.resizerSE.hide();
    this.resizerNW.hide();

    // If there is no elements
    if (!elements || elements.length == 0) {
      // Hide all things and reset all variables
      this.selectedRect.hide();
      this.currentShapes = [];
      this.toMoveShapes = [];
      this.dragBounds = undefined;
      this.oldDragBounds = undefined;
    } else {
      // Set the current Shapes
      this.currentShapes = elements;

      // Get all shapes with the highest parent in object hierarchy (canvas is the top most parent)
      var topLevelElements = this.facade.getCanvas().getShapesWithSharedParent(elements);
      this.toMoveShapes = topLevelElements;

      this.toMoveShapes = this.toMoveShapes.findAll(function (shape) {
        return shape instanceof ORYX.Core.Node && (shape.dockers.length === 0 || !elements.member(shape.dockers.first().getDockedShape()));
      });

      elements.each(
        function (shape) {
          if (!(shape instanceof ORYX.Core.Edge)) {
            return;
          }

          var dks = shape.getDockers();

          var hasF = elements.member(dks.first().getDockedShape());
          var hasL = elements.member(dks.last().getDockedShape());

          //				if(!hasL) {
          //					this.toMoveShapes.push(dks.last());
          //				}
          //				if(!hasF){
          //					this.toMoveShapes.push(dks.first())
          //				}
          /* Enable movement of undocked edges */
          if (!hasF && !hasL) {
            var isUndocked = !dks.first().getDockedShape() && !dks.last().getDockedShape();
            if (isUndocked) {
              this.toMoveShapes = this.toMoveShapes.concat(dks);
            }
          }

          if (shape.dockers.length > 2 && hasF && hasL) {
            this.toMoveShapes = this.toMoveShapes.concat(
              dks.findAll(function (el, index) {
                return index > 0 && index < dks.length - 1;
              })
            );
          }
        }.bind(this)
      );

      // Calculate the new area-bounds of the selection
      var newBounds = undefined;
      this.toMoveShapes.each(
        function (value) {
          var shape = value;
          if (value instanceof ORYX.Core.Controls.Docker) {
            /* Get the Shape */
            shape = value.parent;
          }

          if (!newBounds) {
            newBounds = shape.absoluteBounds();
          } else {
            newBounds.include(shape.absoluteBounds());
          }
        }.bind(this)
      );

      if (!newBounds) {
        elements.each(function (value) {
          if (!newBounds) {
            newBounds = value.absoluteBounds();
          } else {
            newBounds.include(value.absoluteBounds());
          }
        });
      }

      // Set the new bounds
      this.dragBounds = newBounds;
      this.oldDragBounds = newBounds.clone();

      // Update and show the rectangle
      this.resizeRectangle(newBounds);
      this.selectedRect.show();

      // Show the resize button, if there is only one element and this is resizeable
      if (elements.length == 1 && elements[0].isResizable) {
        var aspectRatio = elements[0].getStencil().fixedAspectRatio()
          ? elements[0].bounds.width() / elements[0].bounds.height()
          : undefined;
        this.resizerSE.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio);
        this.resizerSE.show();
        this.resizerNW.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio);
        this.resizerNW.show();
      } else {
        this.resizerSE.setBounds(undefined);
        this.resizerNW.setBounds(undefined);
      }

      // If Snap-To-Grid is enabled, the Snap-Point will be calculate
      if (ORYX.CONFIG.GRID_ENABLED) {
        // Reset all points
        this.distPoints = [];

        if (this.distPointTimeout) window.clearTimeout(this.distPointTimeout);

        this.distPointTimeout = window.setTimeout(
          function () {
            // Get all the shapes, there will consider at snapping
            // Consider only those elements who shares the same parent element
            var distShapes = this.facade
              .getCanvas()
              .getChildShapes(true)
              .findAll(function (value) {
                var parentShape = value.parent;
                while (parentShape) {
                  if (elements.member(parentShape)) return false;
                  parentShape = parentShape.parent;
                }
                return true;
              });

            // The current selection will delete from this array
            //elements.each(function(shape) {
            //	distShapes = distShapes.without(shape);
            //});

            // For all these shapes
            distShapes.each(
              function (value) {
                if (!(value instanceof ORYX.Core.Edge)) {
                  var ul = value.absoluteXY();
                  var width = value.bounds.width();
                  var height = value.bounds.height();

                  // Add the upperLeft, center and lowerRight - Point to the distancePoints
                  this.distPoints.push({
                    ul: {
                      x: ul.x,
                      y: ul.y,
                    },
                    c: {
                      x: ul.x + width / 2,
                      y: ul.y + height / 2,
                    },
                    lr: {
                      x: ul.x + width,
                      y: ul.y + height,
                    },
                  });
                }
              }.bind(this)
            );
          }.bind(this),
          10
        );
      }
    }
  },

  /**
   * Adjust an Point to the Snap Points
   *
   */
  snapToGrid: function (position) {
    // Get the current Bounds
    var bounds = this.dragBounds;

    var point = {};

    var ulThres = 6;
    var cThres = 10;
    var lrThres = 6;

    var scale = this.vLine ? this.vLine.getScale() : 1;

    var ul = { x: position.x / scale, y: position.y / scale };
    var c = { x: position.x / scale + bounds.width() / 2, y: position.y / scale + bounds.height() / 2 };
    var lr = { x: position.x / scale + bounds.width(), y: position.y / scale + bounds.height() };

    var offsetX, offsetY;
    var gridX, gridY;

    // For each distant point
    this.distPoints.each(function (value) {
      var x, y, gx, gy;
      if (Math.abs(value.c.x - c.x) < cThres) {
        x = value.c.x - c.x;
        gx = value.c.x;
      } /* else if (Math.abs(value.ul.x-ul.x) < ulThres){
				x = value.ul.x-ul.x;
				gx = value.ul.x;
			} else if (Math.abs(value.lr.x-lr.x) < lrThres){
				x = value.lr.x-lr.x;
				gx = value.lr.x;
			} */

      if (Math.abs(value.c.y - c.y) < cThres) {
        y = value.c.y - c.y;
        gy = value.c.y;
      } /* else if (Math.abs(value.ul.y-ul.y) < ulThres){
				y = value.ul.y-ul.y;
				gy = value.ul.y;
			} else if (Math.abs(value.lr.y-lr.y) < lrThres){
				y = value.lr.y-lr.y;
				gy = value.lr.y;
			} */

      if (x !== undefined) {
        offsetX = offsetX === undefined ? x : Math.abs(x) < Math.abs(offsetX) ? x : offsetX;
        if (offsetX === x) gridX = gx;
      }

      if (y !== undefined) {
        offsetY = offsetY === undefined ? y : Math.abs(y) < Math.abs(offsetY) ? y : offsetY;
        if (offsetY === y) gridY = gy;
      }
    });

    if (offsetX !== undefined) {
      ul.x += offsetX;
      ul.x *= scale;
      if (this.vLine && gridX) this.vLine.update(gridX);
    } else {
      ul.x = position.x - (position.x % (ORYX.CONFIG.GRID_DISTANCE / 2));
      if (this.vLine) this.vLine.hide();
    }

    if (offsetY !== undefined) {
      ul.y += offsetY;
      ul.y *= scale;
      if (this.hLine && gridY) this.hLine.update(gridY);
    } else {
      ul.y = position.y - (position.y % (ORYX.CONFIG.GRID_DISTANCE / 2));
      if (this.hLine) this.hLine.hide();
    }

    return ul;
  },

  showGridLine: function () {},

  /**
   * Redraw of the Rectangle of the SelectedArea
   * @param {Object} bounds
   */
  resizeRectangle: function (bounds) {
    // Resize the Rectangle
    this.selectedRect.resize(bounds);
  },
});

ORYX.Plugins.SelectedRect = Clazz.extend({
  construct: function (parentId) {
    this.parentId = parentId;

    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', $(parentId), ['g']);

    this.dashedArea = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'rect',
      { x: 0, y: 0, 'stroke-width': 1, stroke: '#777777', fill: 'none', 'stroke-dasharray': '2,2', 'pointer-events': 'none' },
    ]);

    this.hide();
  },

  hide: function () {
    this.node.setAttributeNS(null, 'display', 'none');
  },

  show: function () {
    this.node.setAttributeNS(null, 'display', '');
  },

  resize: function (bounds) {
    var upL = bounds.upperLeft();

    var padding = ORYX.CONFIG.SELECTED_AREA_PADDING;

    this.dashedArea.setAttributeNS(null, 'width', bounds.width() + 2 * padding);
    this.dashedArea.setAttributeNS(null, 'height', bounds.height() + 2 * padding);
    this.node.setAttributeNS(null, 'transform', 'translate(' + (upL.x - padding) + ', ' + (upL.y - padding) + ')');
  },
});

ORYX.Plugins.GridLine = Clazz.extend({
  construct: function (parentId, direction) {
    if (ORYX.Plugins.GridLine.DIR_HORIZONTAL !== direction && ORYX.Plugins.GridLine.DIR_VERTICAL !== direction) {
      direction = ORYX.Plugins.GridLine.DIR_HORIZONTAL;
    }

    this.parent = $(parentId);
    this.direction = direction;
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.parent, ['g']);

    this.line = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
      'path',
      {
        'stroke-width': 1,
        stroke: 'silver',
        fill: 'none',
        'stroke-dasharray': '5,5',
        'pointer-events': 'none',
      },
    ]);

    this.hide();
  },

  hide: function () {
    this.node.setAttributeNS(null, 'display', 'none');
  },

  show: function () {
    this.node.setAttributeNS(null, 'display', '');
  },

  getScale: function () {
    try {
      return this.parent.parentNode.transform.baseVal.getItem(0).matrix.a;
    } catch (e) {
      return 1;
    }
  },

  update: function (pos) {
    if (this.direction === ORYX.Plugins.GridLine.DIR_HORIZONTAL) {
      var y = pos instanceof Object ? pos.y : pos;
      var cWidth = this.parent.parentNode.parentNode.width.baseVal.value / this.getScale();
      this.line.setAttributeNS(null, 'd', 'M 0 ' + y + ' L ' + cWidth + ' ' + y);
    } else {
      var x = pos instanceof Object ? pos.x : pos;
      var cHeight = this.parent.parentNode.parentNode.height.baseVal.value / this.getScale();
      this.line.setAttributeNS(null, 'd', 'M' + x + ' 0 L ' + x + ' ' + cHeight);
    }

    this.show();
  },
});

ORYX.Plugins.GridLine.DIR_HORIZONTAL = 'hor';
ORYX.Plugins.GridLine.DIR_VERTICAL = 'ver';

ORYX.Plugins.Resizer = Clazz.extend({
  construct: function (parentId, orientation, facade) {
    this.parentId = parentId;
    this.orientation = orientation;
    this.facade = facade;

    this.node = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', $('canvasSection'), [
      'div',
      { class: 'resizer_' + this.orientation, style: 'left:0px; top:0px;position:absolute;' },
    ]);

    this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this), true);
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true);
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.handleMouseMove.bind(this), false);

    this.dragEnable = false;
    this.offSetPosition = { x: 0, y: 0 };
    this.bounds = undefined;

    this.canvasNode = this.facade.getCanvas().node;

    this.minSize = undefined;
    this.maxSize = undefined;

    this.aspectRatio = undefined;

    this.resizeCallbacks = [];
    this.resizeStartCallbacks = [];
    this.resizeEndCallbacks = [];
    this.hide();

    // Calculate the Offset
    this.scrollNode = this.node.parentNode.parentNode.parentNode;
  },

  handleMouseDown: function (event) {
    this.dragEnable = true;

    this.offsetScroll = { x: this.scrollNode.scrollLeft, y: this.scrollNode.scrollTop };

    this.offSetPosition = {
      x: Event.pointerX(event) - this.position.x,
      y: Event.pointerY(event) - this.position.y,
    };

    this.resizeStartCallbacks.each(
      function (value) {
        value(this.bounds);
      }.bind(this)
    );
  },

  handleMouseUp: function (event) {
    this.dragEnable = false;
    this.containmentParentNode = null;
    this.resizeEndCallbacks.each(
      function (value) {
        value(this.bounds);
      }.bind(this)
    );
  },

  handleMouseMove: function (event) {
    if (!this.dragEnable) {
      return;
    }

    if (event.shiftKey || event.ctrlKey) {
      this.aspectRatio = this.bounds.width() / this.bounds.height();
    } else {
      this.aspectRatio = undefined;
    }

    var position = {
      x: Event.pointerX(event) - this.offSetPosition.x,
      y: Event.pointerY(event) - this.offSetPosition.y,
    };

    position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft;
    position.y -= this.offsetScroll.y - this.scrollNode.scrollTop;

    position.x = Math.min(position.x, this.facade.getCanvas().bounds.width());
    position.y = Math.min(position.y, this.facade.getCanvas().bounds.height());

    var offset = {
      x: position.x - this.position.x,
      y: position.y - this.position.y,
    };

    if (this.aspectRatio) {
      // fixed aspect ratio
      newAspectRatio = (this.bounds.width() + offset.x) / (this.bounds.height() + offset.y);
      if (newAspectRatio > this.aspectRatio) {
        offset.x = this.aspectRatio * (this.bounds.height() + offset.y) - this.bounds.width();
      } else if (newAspectRatio < this.aspectRatio) {
        offset.y = (this.bounds.width() + offset.x) / this.aspectRatio - this.bounds.height();
      }
    }

    // respect minimum and maximum sizes of stencil
    if (this.orientation === 'northwest') {
      if (this.bounds.width() - offset.x > this.maxSize.width) {
        offset.x = -(this.maxSize.width - this.bounds.width());
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x;
      }
      if (this.bounds.width() - offset.x < this.minSize.width) {
        offset.x = -(this.minSize.width - this.bounds.width());
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x;
      }
      if (this.bounds.height() - offset.y > this.maxSize.height) {
        offset.y = -(this.maxSize.height - this.bounds.height());
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio;
      }
      if (this.bounds.height() - offset.y < this.minSize.height) {
        offset.y = -(this.minSize.height - this.bounds.height());
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio;
      }
    } else {
      // defaults to southeast
      if (this.bounds.width() + offset.x > this.maxSize.width) {
        offset.x = this.maxSize.width - this.bounds.width();
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x;
      }
      if (this.bounds.width() + offset.x < this.minSize.width) {
        offset.x = this.minSize.width - this.bounds.width();
        if (this.aspectRatio) offset.y = this.aspectRatio * offset.x;
      }
      if (this.bounds.height() + offset.y > this.maxSize.height) {
        offset.y = this.maxSize.height - this.bounds.height();
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio;
      }
      if (this.bounds.height() + offset.y < this.minSize.height) {
        offset.y = this.minSize.height - this.bounds.height();
        if (this.aspectRatio) offset.x = offset.y / this.aspectRatio;
      }
    }

    if (this.orientation === 'northwest') {
      this.bounds.extend({ x: -offset.x, y: -offset.y });
      this.bounds.moveBy(offset);
    } else {
      // defaults to southeast
      this.bounds.extend(offset);
    }

    this.update();

    this.resizeCallbacks.each(
      function (value) {
        value(this.bounds);
      }.bind(this)
    );

    Event.stop(event);
  },

  registerOnResizeStart: function (callback) {
    if (!this.resizeStartCallbacks.member(callback)) {
      this.resizeStartCallbacks.push(callback);
    }
  },

  unregisterOnResizeStart: function (callback) {
    if (this.resizeStartCallbacks.member(callback)) {
      this.resizeStartCallbacks = this.resizeStartCallbacks.without(callback);
    }
  },

  registerOnResizeEnd: function (callback) {
    if (!this.resizeEndCallbacks.member(callback)) {
      this.resizeEndCallbacks.push(callback);
    }
  },

  unregisterOnResizeEnd: function (callback) {
    if (this.resizeEndCallbacks.member(callback)) {
      this.resizeEndCallbacks = this.resizeEndCallbacks.without(callback);
    }
  },

  registerOnResize: function (callback) {
    if (!this.resizeCallbacks.member(callback)) {
      this.resizeCallbacks.push(callback);
    }
  },

  unregisterOnResize: function (callback) {
    if (this.resizeCallbacks.member(callback)) {
      this.resizeCallbacks = this.resizeCallbacks.without(callback);
    }
  },

  hide: function () {
    this.node.style.display = 'none';
  },

  show: function () {
    if (this.bounds) this.node.style.display = '';
  },

  setBounds: function (bounds, min, max, aspectRatio) {
    this.bounds = bounds;

    if (!min) min = { width: ORYX.CONFIG.MINIMUM_SIZE, height: ORYX.CONFIG.MINIMUM_SIZE };

    if (!max) max = { width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE };

    this.minSize = min;
    this.maxSize = max;

    this.aspectRatio = aspectRatio;

    this.update();
  },

  update: function () {
    if (!this.bounds) {
      return;
    }

    var upL = this.bounds.upperLeft();

    if (this.bounds.width() < this.minSize.width) {
      this.bounds.set(upL.x, upL.y, upL.x + this.minSize.width, upL.y + this.bounds.height());
    }
    if (this.bounds.height() < this.minSize.height) {
      this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.minSize.height);
    }
    if (this.bounds.width() > this.maxSize.width) {
      this.bounds.set(upL.x, upL.y, upL.x + this.maxSize.width, upL.y + this.bounds.height());
    }
    if (this.bounds.height() > this.maxSize.height) {
      this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.maxSize.height);
    }

    var a = this.canvasNode.getScreenCTM();

    upL.x *= a.a;
    upL.y *= a.d;

    var additionalIEZoom = 1;
    if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
      var ua = navigator.userAgent;
      if (ua.indexOf('MSIE') >= 0) {
        //IE 10 and below
        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
        if (zoom !== 100) {
          additionalIEZoom = zoom / 100;
        }
      }
    }

    if (additionalIEZoom === 1) {
      upL.y = upL.y - jQuery('#canvasSection').offset().top + a.f;
      upL.x = upL.x - jQuery('#canvasSection').offset().left + a.e;
    } else {
      var canvasOffsetLeft = jQuery('#canvasSection').offset().left;
      var canvasScrollLeft = jQuery('#canvasSection').scrollLeft();
      var canvasScrollTop = jQuery('#canvasSection').scrollTop();

      var offset = a.e - canvasOffsetLeft * additionalIEZoom;
      var additionaloffset = 0;
      if (offset > 10) {
        additionaloffset = offset / additionalIEZoom - offset;
      }
      upL.y =
        upL.y - jQuery('#canvasSection').offset().top * additionalIEZoom + (canvasScrollTop * additionalIEZoom - canvasScrollTop) + a.f;
      upL.x =
        upL.x - canvasOffsetLeft * additionalIEZoom + additionaloffset + (canvasScrollLeft * additionalIEZoom - canvasScrollLeft) + a.e;
    }

    if (this.orientation === 'northwest') {
      upL.x -= 13;
      upL.y -= 13;
    } else {
      // defaults to southeast
      upL.x += a.a * this.bounds.width() + 3;
      upL.y += a.d * this.bounds.height() + 3;
    }

    this.position = upL;

    this.node.style.left = this.position.x + 'px';
    this.node.style.top = this.position.y + 'px';
  },
});

/**
 * Implements a Command to move shapes
 *
 */
ORYX.Core.Command.Move = ORYX.Core.Command.extend({
  construct: function (moveShapes, offset, parent, selectedShapes, plugin) {
    this.moveShapes = moveShapes;
    this.selectedShapes = selectedShapes;
    this.offset = offset;
    this.plugin = plugin;
    // Defines the old/new parents for the particular shape
    this.newParents = moveShapes.collect(function (t) {
      return parent || t.parent;
    });
    this.oldParents = moveShapes.collect(function (shape) {
      return shape.parent;
    });
    this.dockedNodes = moveShapes
      .findAll(function (shape) {
        return shape instanceof ORYX.Core.Node && shape.dockers.length == 1;
      })
      .collect(function (shape) {
        return { docker: shape.dockers[0], dockedShape: shape.dockers[0].getDockedShape(), refPoint: shape.dockers[0].referencePoint };
      });
  },
  execute: function () {
    this.dockAllShapes();
    // Moves by the offset
    this.move(this.offset);
    // Addes to the new parents
    this.addShapeToParent(this.newParents);
    // Set the selection to the current selection
    this.selectCurrentShapes();
    this.plugin.facade.getCanvas().update();
    this.plugin.facade.updateSelection();
  },
  rollback: function () {
    // Moves by the inverted offset
    var offset = { x: -this.offset.x, y: -this.offset.y };
    this.move(offset);
    // Addes to the old parents
    this.addShapeToParent(this.oldParents);
    this.dockAllShapes(true);

    // Set the selection to the current selection
    this.selectCurrentShapes();
    this.plugin.facade.getCanvas().update();
    this.plugin.facade.updateSelection();
  },
  move: function (offset, doLayout) {
    // Move all Shapes by these offset
    for (var i = 0; i < this.moveShapes.length; i++) {
      var value = this.moveShapes[i];
      value.bounds.moveBy(offset);

      if (value instanceof ORYX.Core.Node) {
        (value.dockers || []).each(function (d) {
          d.bounds.moveBy(offset);
        });

        // Update all Dockers of Child shapes
        /*var childShapesNodes = value.getChildShapes(true).findAll(function(shape){ return shape instanceof ORYX.Core.Node });							
				var childDockedShapes = childShapesNodes.collect(function(shape){ return shape.getAllDockedShapes() }).flatten().uniq();							
				var childDockedEdge = childDockedShapes.findAll(function(shape){ return shape instanceof ORYX.Core.Edge });							
				childDockedEdge = childDockedEdge.findAll(function(shape){ return shape.getAllDockedShapes().all(function(dsh){ return childShapesNodes.include(dsh) }) });							
				var childDockedDockers = childDockedEdge.collect(function(shape){ return shape.dockers }).flatten();
				
				for (var j = 0; j < childDockedDockers.length; j++) {
					var docker = childDockedDockers[j];
					if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
						//docker.bounds.moveBy(offset);
						//docker.update();
					}
				}*/

        var allEdges = []
          .concat(value.getIncomingShapes())
          .concat(value.getOutgoingShapes())
          // Remove all edges which are included in the selection from the list
          .findAll(
            function (r) {
              return (
                r instanceof ORYX.Core.Edge &&
                !this.moveShapes.any(function (d) {
                  return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r);
                })
              );
            }.bind(this)
          )
          // Remove all edges which are between the node and a node contained in the selection from the list
          .findAll(
            function (r) {
              return (
                (r.dockers.first().getDockedShape() == value || !this.moveShapes.include(r.dockers.first().getDockedShape())) &&
                (r.dockers.last().getDockedShape() == value || !this.moveShapes.include(r.dockers.last().getDockedShape()))
              );
            }.bind(this)
          );

        // Layout all outgoing/incoming edges
        this.plugin.layoutEdges(value, allEdges, offset);

        var allSameEdges = []
          .concat(value.getIncomingShapes())
          .concat(value.getOutgoingShapes())
          // Remove all edges which are included in the selection from the list
          .findAll(
            function (r) {
              return (
                r instanceof ORYX.Core.Edge &&
                r.dockers.first().isDocked() &&
                r.dockers.last().isDocked() &&
                !this.moveShapes.include(r) &&
                !this.moveShapes.any(function (d) {
                  return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r);
                })
              );
            }.bind(this)
          )
          // Remove all edges which are included in the selection from the list
          .findAll(
            function (r) {
              return (
                this.moveShapes.indexOf(r.dockers.first().getDockedShape()) > i ||
                this.moveShapes.indexOf(r.dockers.last().getDockedShape()) > i
              );
            }.bind(this)
          );

        for (var j = 0; j < allSameEdges.length; j++) {
          for (var k = 1; k < allSameEdges[j].dockers.length - 1; k++) {
            var docker = allSameEdges[j].dockers[k];
            if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
              docker.bounds.moveBy(offset);
            }
          }
        }

        /*var i=-1;
				var nodes = value.getChildShapes(true);
				var allEdges = [];
				while(++i<nodes.length){
					var edges = [].concat(nodes[i].getIncomingShapes())
						.concat(nodes[i].getOutgoingShapes())
						// Remove all edges which are included in the selection from the list
						.findAll(function(r){ return r instanceof ORYX.Core.Edge && !allEdges.include(r) && r.dockers.any(function(d){ return !value.bounds.isIncluded(d.bounds.center)})})
					allEdges = allEdges.concat(edges);
					if (edges.length <= 0){ continue }
					//this.plugin.layoutEdges(nodes[i], edges, offset);
				}*/
      }
    }
  },
  dockAllShapes: function (shouldDocked) {
    // Undock all Nodes
    for (var i = 0; i < this.dockedNodes.length; i++) {
      var docker = this.dockedNodes[i].docker;

      docker.setDockedShape(shouldDocked ? this.dockedNodes[i].dockedShape : undefined);
      if (docker.getDockedShape()) {
        docker.setReferencePoint(this.dockedNodes[i].refPoint);
        //docker.update();
      }
    }
  },

  addShapeToParent: function (parents) {
    // For every Shape, add this and reset the position
    for (var i = 0; i < this.moveShapes.length; i++) {
      var currentShape = this.moveShapes[i];
      if (currentShape instanceof ORYX.Core.Node && currentShape.parent !== parents[i]) {
        // Calc the new position
        var unul = parents[i].absoluteXY();
        var csul = currentShape.absoluteXY();
        var x = csul.x - unul.x;
        var y = csul.y - unul.y;

        // Add the shape to the new contained shape
        parents[i].add(currentShape);
        // Add all attached shapes as well
        currentShape.getOutgoingShapes(
          function (shape) {
            if (shape instanceof ORYX.Core.Node && !this.moveShapes.member(shape)) {
              parents[i].add(shape);
            }
          }.bind(this)
        );

        // Set the new position
        if (currentShape instanceof ORYX.Core.Node && currentShape.dockers.length == 1) {
          var b = currentShape.bounds;
          x += b.width() / 2;
          y += b.height() / 2;
          currentShape.dockers.first().bounds.centerMoveTo(x, y);
        } else {
          currentShape.bounds.moveTo(x, y);
        }
      }

      // Update the shape
      //currentShape.update();
    }
  },
  selectCurrentShapes: function () {
    this.plugin.facade.setSelection(this.selectedShapes);
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.DragDocker = Clazz.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function (facade) {
    this.facade = facade;

    // Set the valid and invalid color
    this.VALIDCOLOR = ORYX.CONFIG.SELECTION_VALID_COLOR;
    this.INVALIDCOLOR = ORYX.CONFIG.SELECTION_INVALID_COLOR;

    // Define Variables
    this.shapeSelection = undefined;
    this.docker = undefined;
    this.dockerParent = undefined;
    this.dockerSource = undefined;
    this.dockerTarget = undefined;
    this.lastUIObj = undefined;
    this.isStartDocker = undefined;
    this.isEndDocker = undefined;
    this.undockTreshold = 10;
    this.initialDockerPosition = undefined;
    this.outerDockerNotMoved = undefined;
    this.isValid = false;

    // For the Drag and Drop
    // Register on MouseDown-Event on a Docker
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DOCKERDRAG, this.handleDockerDrag.bind(this));

    // Register on over/out to show / hide a docker
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOVER, this.handleMouseOver.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOUT, this.handleMouseOut.bind(this));
  },

  /**
   * MouseOut Handler
   *
   */
  handleMouseOut: function (event, uiObj) {
    // If there is a Docker, hide this
    if (!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
      uiObj.hide();
    } else if (!this.docker && uiObj instanceof ORYX.Core.Edge) {
      uiObj.dockers.each(function (docker) {
        docker.hide();
      });
    }
  },

  /**
   * MouseOver Handler
   *
   */
  handleMouseOver: function (event, uiObj) {
    // If there is a Docker, show this
    if (!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
      uiObj.show();
    } else if (!this.docker && uiObj instanceof ORYX.Core.Edge) {
      uiObj.dockers.each(function (docker) {
        docker.show();
      });
    }
  },
  /**
   * DockerDrag Handler
   * delegates the uiEvent of the drag event to the mouseDown function
   */
  handleDockerDrag: function (event, uiObj) {
    this.handleMouseDown(event.uiEvent, uiObj);
  },

  /**
   * MouseDown Handler
   *
   */
  handleMouseDown: function (event, uiObj) {
    // If there is a Docker
    if (uiObj instanceof ORYX.Core.Controls.Docker && uiObj.isMovable) {
      /* Buffering shape selection and clear selection*/
      this.shapeSelection = this.facade.getSelection();
      this.facade.setSelection();

      this.docker = uiObj;
      this.initialDockerPosition = this.docker.bounds.center();
      this.outerDockerNotMoved = false;
      this.dockerParent = uiObj.parent;

      // Define command arguments
      this._commandArg = { docker: uiObj, dockedShape: uiObj.getDockedShape(), refPoint: uiObj.referencePoint || uiObj.bounds.center() };

      // Show the Docker
      this.docker.show();

      // If the Dockers Parent is an Edge,
      //  and the Docker is either the first or last Docker of the Edge
      if (uiObj.parent instanceof ORYX.Core.Edge && (uiObj.parent.dockers.first() == uiObj || uiObj.parent.dockers.last() == uiObj)) {
        // Get the Edge Source or Target
        if (uiObj.parent.dockers.first() == uiObj && uiObj.parent.dockers.last().getDockedShape()) {
          this.dockerTarget = uiObj.parent.dockers.last().getDockedShape();
        } else if (uiObj.parent.dockers.last() == uiObj && uiObj.parent.dockers.first().getDockedShape()) {
          this.dockerSource = uiObj.parent.dockers.first().getDockedShape();
        }
      } else {
        // If there parent is not an Edge, undefined the Source and Target
        this.dockerSource = undefined;
        this.dockerTarget = undefined;
      }

      this.isStartDocker = this.docker.parent.dockers.first() === this.docker;
      this.isEndDocker = this.docker.parent.dockers.last() === this.docker;

      // add to canvas while dragging
      this.facade.getCanvas().add(this.docker.parent);

      // Hide all Labels from Docker
      this.docker.parent.getLabels().each(function (label) {
        label.hide();
      });

      var eventCoordinates = this.facade.eventCoordinates(event);
      var additionalIEZoom = 1;
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent;
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100;
          }
        }
      }

      if (additionalIEZoom !== 1) {
        eventCoordinates.x = eventCoordinates.x / additionalIEZoom;
        eventCoordinates.y = eventCoordinates.y / additionalIEZoom;
      }

      // Undocked the Docker from current Shape
      if ((!this.isStartDocker && !this.isEndDocker) || !this.docker.isDocked()) {
        this.docker.setDockedShape(undefined);
        // Set the Docker to the center of the mouse pointer
        this.docker.bounds.centerMoveTo(eventCoordinates);
        this.dockerParent._update();
      } else {
        this.outerDockerNotMoved = true;
      }

      var option = { movedCallback: this.dockerMoved.bind(this), upCallback: this.dockerMovedFinished.bind(this) };

      this.startEventPos = eventCoordinates;

      // Enable the Docker for Drag'n'Drop, give the mouseMove and mouseUp-Callback with
      ORYX.Core.UIEnableDrag(event, uiObj, option);
    }
  },

  /**
   * Docker MouseMove Handler
   *
   */
  dockerMoved: function (event) {
    this.outerDockerNotMoved = false;
    var snapToMagnet = undefined;

    if (this.docker.parent) {
      if (this.isStartDocker || this.isEndDocker) {
        // Get the EventPosition and all Shapes on these point
        var evPos = this.facade.eventCoordinates(event);

        var additionalIEZoom = 1;
        if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
          var ua = navigator.userAgent;
          if (ua.indexOf('MSIE') >= 0) {
            //IE 10 and below
            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
            if (zoom !== 100) {
              additionalIEZoom = zoom / 100;
            }
          }
        }

        if (additionalIEZoom !== 1) {
          evPos.x = evPos.x / additionalIEZoom;
          evPos.y = evPos.y / additionalIEZoom;
        }

        if (this.docker.isDocked()) {
          /* Only consider start/end dockers if they are moved over a treshold */
          var distanceDockerPointer = ORYX.Core.Math.getDistancePointToPoint(evPos, this.initialDockerPosition);
          if (distanceDockerPointer < this.undockTreshold) {
            this.outerDockerNotMoved = true;
            return;
          }

          /* Undock the docker */
          this.docker.setDockedShape(undefined);
          // Set the Docker to the center of the mouse pointer
          //this.docker.bounds.centerMoveTo(evPos);
          this.dockerParent._update();
        }

        var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos);

        // Get the top level Shape on these, but not the same as Dockers parent
        var uiObj = shapes.pop();
        if (this.docker.parent === uiObj) {
          uiObj = shapes.pop();
        }

        // If the top level Shape the same as the last Shape, then return
        if (this.lastUIObj == uiObj) {
          //return;
          // If the top level uiObj instance of Shape and this isn't the parent of the docker
        } else if (uiObj instanceof ORYX.Core.Shape) {
          // Ask by the StencilSet if the source, the edge and the target valid connections.
          if (this.docker.parent instanceof ORYX.Core.Edge) {
            var highestParent = this.getHighestParentBeforeCanvas(uiObj);
            /* Ensure that the shape to dock is not a child shape
             * of the same edge.
             */
            if (highestParent instanceof ORYX.Core.Edge && this.docker.parent === highestParent) {
              this.isValid = false;
              this.dockerParent._update();
              return;
            }
            this.isValid = false;
            var curObj = uiObj,
              orgObj = uiObj;
            while (!this.isValid && curObj && !(curObj instanceof ORYX.Core.Canvas)) {
              uiObj = curObj;
              this.isValid = this.facade.getRules().canConnect({
                sourceShape: this.dockerSource // Is there a docked source
                  ? this.dockerSource // than set this
                  : this.isStartDocker // if not and if the Docker is the start docker
                  ? uiObj // take the last uiObj
                  : undefined, // if not set it to undefined;
                edgeShape: this.docker.parent,
                targetShape: this.dockerTarget // Is there a docked target
                  ? this.dockerTarget // than set this
                  : this.isEndDocker // if not and if the Docker is not the start docker
                  ? uiObj // take the last uiObj
                  : undefined, // if not set it to undefined;
              });
              curObj = curObj.parent;
            }

            // Reset uiObj if no
            // valid parent is found
            if (!this.isValid) {
              uiObj = orgObj;
            }
          } else {
            this.isValid = this.facade.getRules().canConnect({
              sourceShape: uiObj,
              edgeShape: this.docker.parent,
              targetShape: this.docker.parent,
            });
          }

          // If there is a lastUIObj, hide the magnets
          if (this.lastUIObj) {
            this.hideMagnets(this.lastUIObj);
          }

          // If there is a valid connection, show the magnets
          if (this.isValid) {
            this.showMagnets(uiObj);
          }

          // Set the Highlight Rectangle by these value
          this.showHighlight(uiObj, this.isValid ? this.VALIDCOLOR : this.INVALIDCOLOR);

          // Buffer the current Shape
          this.lastUIObj = uiObj;
        } else {
          // If there is no top level Shape, then hide the highligting of the last Shape
          this.hideHighlight();
          this.lastUIObj ? this.hideMagnets(this.lastUIObj) : null;
          this.lastUIObj = undefined;
          this.isValid = false;
        }

        // Snap to the nearest Magnet
        if (this.lastUIObj && this.isValid && !(event.shiftKey || event.ctrlKey)) {
          snapToMagnet = this.lastUIObj.magnets.find(function (magnet) {
            return magnet.absoluteBounds().isIncluded(evPos);
          });

          if (snapToMagnet) {
            this.docker.bounds.centerMoveTo(snapToMagnet.absoluteCenterXY());
            //this.docker.update()
          }
        }
      }
    }
    // Snap to on the nearest Docker of the same parent
    if (!(event.shiftKey || event.ctrlKey) && !snapToMagnet) {
      var minOffset = ORYX.CONFIG.DOCKER_SNAP_OFFSET;
      var nearestX = minOffset + 1;
      var nearestY = minOffset + 1;

      var dockerCenter = this.docker.bounds.center();

      if (this.docker.parent) {
        this.docker.parent.dockers.each(
          function (docker) {
            if (this.docker == docker) {
              return;
            }

            var center = docker.referencePoint ? docker.getAbsoluteReferencePoint() : docker.bounds.center();

            nearestX = Math.abs(nearestX) > Math.abs(center.x - dockerCenter.x) ? center.x - dockerCenter.x : nearestX;
            nearestY = Math.abs(nearestY) > Math.abs(center.y - dockerCenter.y) ? center.y - dockerCenter.y : nearestY;
          }.bind(this)
        );

        if (Math.abs(nearestX) < minOffset || Math.abs(nearestY) < minOffset) {
          nearestX = Math.abs(nearestX) < minOffset ? nearestX : 0;
          nearestY = Math.abs(nearestY) < minOffset ? nearestY : 0;

          this.docker.bounds.centerMoveTo(dockerCenter.x + nearestX, dockerCenter.y + nearestY);
          //this.docker.update()
        } else {
          var previous = this.docker.parent.dockers[Math.max(this.docker.parent.dockers.indexOf(this.docker) - 1, 0)];
          var next =
            this.docker.parent.dockers[
              Math.min(this.docker.parent.dockers.indexOf(this.docker) + 1, this.docker.parent.dockers.length - 1)
            ];

          if (previous && next && previous !== this.docker && next !== this.docker) {
            var cp = previous.bounds.center();
            var cn = next.bounds.center();
            var cd = this.docker.bounds.center();

            // Checks if the point is on the line between previous and next
            if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 10)) {
              // Get the rise
              var raise = (Number(cn.y) - Number(cp.y)) / (Number(cn.x) - Number(cp.x));
              // Calculate the intersection point
              var intersecX = (cp.y - cp.x * raise - (cd.y - cd.x * -Math.pow(raise, -1))) / (-Math.pow(raise, -1) - raise);
              var intersecY = cp.y - cp.x * raise + raise * intersecX;

              if (isNaN(intersecX) || isNaN(intersecY)) {
                return;
              }

              this.docker.bounds.centerMoveTo(intersecX, intersecY);
            }
          }
        }
      }
    }
    //this.facade.getCanvas().update();
    this.dockerParent._update();
  },

  /**
   * Docker MouseUp Handler
   *
   */
  dockerMovedFinished: function (event) {
    /* Reset to buffered shape selection */
    this.facade.setSelection(this.shapeSelection);

    // Hide the border
    this.hideHighlight();

    // Show all Labels from Docker
    this.dockerParent.getLabels().each(function (label) {
      label.show();
      //label.update();
    });

    // If there is a last top level Shape
    if (this.lastUIObj && (this.isStartDocker || this.isEndDocker)) {
      // If there is a valid connection, the set as a docked Shape to them
      if (this.isValid) {
        this.docker.setDockedShape(this.lastUIObj);

        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED,
          docker: this.docker,
          parent: this.docker.parent,
          target: this.lastUIObj,
        });
      }

      this.hideMagnets(this.lastUIObj);
    }

    // Hide the Docker
    this.docker.hide();

    if (this.outerDockerNotMoved) {
      // Get the EventPosition and all Shapes on these point
      var evPos = this.facade.eventCoordinates(event);
      var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos);

      /* Remove edges from selection */
      var shapeWithoutEdges = shapes.findAll(function (node) {
        return node instanceof ORYX.Core.Node;
      });
      shapes = shapeWithoutEdges.length ? shapeWithoutEdges : shapes;
      this.facade.setSelection(shapes);
    } else {
      //Command-Pattern for dragging one docker
      var dragDockerCommand = ORYX.Core.Command.extend({
        construct: function (docker, newPos, oldPos, newDockedShape, oldDockedShape, facade) {
          this.docker = docker;
          this.index = docker.parent.dockers.indexOf(docker);
          this.newPosition = newPos;
          this.newDockedShape = newDockedShape;
          this.oldPosition = oldPos;
          this.oldDockedShape = oldDockedShape;
          this.facade = facade;
          this.index = docker.parent.dockers.indexOf(docker);
          this.shape = docker.parent;
        },
        execute: function () {
          if (!this.docker.parent) {
            this.docker = this.shape.dockers[this.index];
          }
          this.dock(this.newDockedShape, this.newPosition);
          this.removedDockers = this.shape.removeUnusedDockers();
          this.facade.updateSelection();
        },
        rollback: function () {
          this.dock(this.oldDockedShape, this.oldPosition);
          (this.removedDockers || $H({})).each(
            function (d) {
              this.shape.add(d.value, Number(d.key));
              this.shape._update(true);
            }.bind(this)
          );
          this.facade.updateSelection();
        },
        dock: function (toDockShape, pos) {
          // Set the Docker to the new Shape
          this.docker.setDockedShape(undefined);
          if (toDockShape) {
            this.docker.setDockedShape(toDockShape);
            this.docker.setReferencePoint(pos);
            //this.docker.update();
            //this.docker.parent._update();
          } else {
            this.docker.bounds.centerMoveTo(pos);
          }

          this.facade.getCanvas().update();
        },
      });

      if (this.docker.parent) {
        // Instanziate the dockCommand
        var command = new dragDockerCommand(
          this.docker,
          this.docker.getDockedShape() ? this.docker.referencePoint : this.docker.bounds.center(),
          this._commandArg.refPoint,
          this.docker.getDockedShape(),
          this._commandArg.dockedShape,
          this.facade
        );
        this.facade.executeCommands([command]);
      }
    }

    // Update all Shapes
    //this.facade.updateSelection();

    // Undefined all variables
    this.docker = undefined;
    this.dockerParent = undefined;
    this.dockerSource = undefined;
    this.dockerTarget = undefined;
    this.lastUIObj = undefined;
  },

  /**
   * Hide the highlighting
   */
  hideHighlight: function () {
    this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'validDockedShape' });
  },

  /**
   * Show the highlighting
   *
   */
  showHighlight: function (uiObj, color) {
    this.facade.raiseEvent({
      type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
      highlightId: 'validDockedShape',
      elements: [uiObj],
      color: color,
    });
  },

  showMagnets: function (uiObj) {
    uiObj.magnets.each(function (magnet) {
      magnet.show();
    });
  },

  hideMagnets: function (uiObj) {
    uiObj.magnets.each(function (magnet) {
      magnet.hide();
    });
  },

  getHighestParentBeforeCanvas: function (shape) {
    if (!(shape instanceof ORYX.Core.Shape)) {
      return undefined;
    }

    var parent = shape.parent;
    while (parent && !(parent.parent instanceof ORYX.Core.Canvas)) {
      parent = parent.parent;
    }

    return parent;
  },
});

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.AddDocker = Clazz.extend({
  /**
   *	Constructor
   *	@param {Object} Facade: The Facade of the Editor
   */
  construct: function (facade) {
    this.facade = facade;
    this.enableAdd = false;
    this.enableRemove = false;

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
  },

  setEnableAdd: function (enable) {
    this.enableAdd = enable;

    if (this.enableAdd) {
      jQuery('#add-bendpoint-button').addClass('pressed');
    } else {
      jQuery('#add-bendpoint-button').removeClass('pressed');
      jQuery('#add-bendpoint-button').blur();
    }
  },
  setEnableRemove: function (enable) {
    this.enableRemove = enable;

    if (this.enableRemove) {
      jQuery('#remove-bendpoint-button').addClass('pressed');
    } else {
      jQuery('#remove-bendpoint-button').removeClass('pressed');
      jQuery('#remove-bendpoint-button').blur();
    }
  },

  enabledAdd: function (enable) {
    return this.enableAdd;
  },
  enabledRemove: function () {
    return this.enableRemove;
  },

  /**
   * MouseDown Handler
   *
   */
  handleMouseDown: function (event, uiObj) {
    if (this.enabledAdd() && uiObj instanceof ORYX.Core.Edge) {
      this.newDockerCommand({
        edge: uiObj,
        position: this.facade.eventCoordinates(event),
      });
      this.setEnableAdd(false);
    } else if (this.enabledRemove() && uiObj instanceof ORYX.Core.Controls.Docker && uiObj.parent instanceof ORYX.Core.Edge) {
      this.newDockerCommand({
        edge: uiObj.parent,
        docker: uiObj,
      });
      this.setEnableRemove(false);
    }
    document.body.style.cursor = 'default';
  },

  // Options: edge (required), position (required if add), docker (required if delete)
  newDockerCommand: function (options) {
    if (!options.edge) return;

    var commandClass = ORYX.Core.Command.extend({
      construct: function (addEnabled, deleteEnabled, edge, docker, pos, facade) {
        this.addEnabled = addEnabled;
        this.deleteEnabled = deleteEnabled;
        this.edge = edge;
        this.docker = docker;
        this.pos = pos;
        this.facade = facade;
      },
      execute: function () {
        if (this.addEnabled) {
          if (!this.docker) {
            this.docker = this.edge.addDocker(this.pos);
            this.index = this.edge.dockers.indexOf(this.docker);
          } else {
            this.edge.add(this.docker, this.index);
          }
        } else if (this.deleteEnabled) {
          this.index = this.edge.dockers.indexOf(this.docker);
          this.pos = this.docker.bounds.center();
          this.edge.removeDocker(this.docker);
        }
        this.edge.getLabels().invoke('show');
        this.facade.getCanvas().update();
        this.facade.updateSelection();
      },
      rollback: function () {
        if (this.addEnabled) {
          if (this.docker instanceof ORYX.Core.Controls.Docker) {
            this.edge.removeDocker(this.docker);
          }
        } else if (this.deleteEnabled) {
          this.edge.add(this.docker, this.index);
        }
        this.edge.getLabels().invoke('show');
        this.facade.getCanvas().update();
        this.facade.updateSelection();
      },
    });

    var command = new commandClass(this.enabledAdd(), this.enabledRemove(), options.edge, options.docker, options.position, this.facade);

    this.facade.executeCommands([command]);
  },
});

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.SelectionFrame = Clazz.extend({
  construct: function (facade) {
    this.facade = facade;

    // Register on MouseEvents
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
    document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true);

    // Some initiale variables
    this.position = { x: 0, y: 0 };
    this.size = { width: 0, height: 0 };
    this.offsetPosition = { x: 0, y: 0 };

    // (Un)Register Mouse-Move Event
    this.moveCallback = undefined;
    this.offsetScroll = { x: 0, y: 0 };
    // HTML-Node of Selection-Frame
    this.node = ORYX.Editor.graft('http://www.w3.org/1999/xhtml', $('canvasSection'), ['div', { class: 'Oryx_SelectionFrame' }]);

    this.hide();
  },

  handleMouseDown: function (event, uiObj) {
    // If there is the Canvas
    if (uiObj instanceof ORYX.Core.Canvas) {
      // Calculate the Offset
      var scrollNode = uiObj.rootNode.parentNode.parentNode;

      var a = this.facade.getCanvas().node.getScreenCTM();
      this.offsetPosition = {
        x: a.e,
        y: a.f,
      };

      // Set the new Position
      this.setPos({
        x: Event.pointerX(event) - jQuery('#canvasSection').offset().left,
        y: Event.pointerY(event) - jQuery('#canvasSection').offset().top + 5,
      });

      // Reset the size
      this.resize({ width: 0, height: 0 });
      this.moveCallback = this.handleMouseMove.bind(this);

      // Register Mouse-Move Event
      document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false);

      this.offsetScroll = { x: scrollNode.scrollLeft, y: scrollNode.scrollTop };

      // Show the Frame
      this.show();
    }

    Event.stop(event);
  },

  handleMouseUp: function (event) {
    // If there was an MouseMoving
    if (this.moveCallback) {
      // Hide the Frame
      this.hide();

      // Unregister Mouse-Move
      document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false);

      this.moveCallback = undefined;

      var corrSVG = this.facade.getCanvas().node.getScreenCTM();

      // Calculate the positions of the Frame
      var a = {
        x: this.size.width > 0 ? this.position.x : this.position.x + this.size.width,
        y: this.size.height > 0 ? this.position.y : this.position.y + this.size.height,
      };

      var b = {
        x: a.x + Math.abs(this.size.width),
        y: a.y + Math.abs(this.size.height),
      };

      var additionalIEZoom = 1;
      if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
        var ua = navigator.userAgent;
        if (ua.indexOf('MSIE') >= 0) {
          //IE 10 and below
          var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
          if (zoom !== 100) {
            additionalIEZoom = zoom / 100;
          }
        }
      }

      if (additionalIEZoom === 1) {
        a.x = a.x - (corrSVG.e - jQuery('#canvasSection').offset().left);
        a.y = a.y - (corrSVG.f - jQuery('#canvasSection').offset().top);
        b.x = b.x - (corrSVG.e - jQuery('#canvasSection').offset().left);
        b.y = b.y - (corrSVG.f - jQuery('#canvasSection').offset().top);
      } else {
        var canvasOffsetLeft = jQuery('#canvasSection').offset().left;
        var canvasScrollLeft = jQuery('#canvasSection').scrollLeft();
        var canvasScrollTop = jQuery('#canvasSection').scrollTop();

        var offset = a.e - canvasOffsetLeft * additionalIEZoom;
        var additionaloffset = 0;
        if (offset > 10) {
          additionaloffset = offset / additionalIEZoom - offset;
        }

        a.x =
          a.x -
          (corrSVG.e - canvasOffsetLeft * additionalIEZoom + additionaloffset + (canvasScrollLeft * additionalIEZoom - canvasScrollLeft));
        a.y =
          a.y -
          (corrSVG.f - jQuery('#canvasSection').offset().top * additionalIEZoom + (canvasScrollTop * additionalIEZoom - canvasScrollTop));
        b.x =
          b.x -
          (corrSVG.e - canvasOffsetLeft * additionalIEZoom + additionaloffset + (canvasScrollLeft * additionalIEZoom - canvasScrollLeft));
        b.y =
          b.y -
          (corrSVG.f - jQuery('#canvasSection').offset().top * additionalIEZoom + (canvasScrollTop * additionalIEZoom - canvasScrollTop));
      }

      // Fit to SVG-Coordinates
      a.x /= corrSVG.a;
      a.y /= corrSVG.d;
      b.x /= corrSVG.a;
      b.y /= corrSVG.d;

      // Calculate the elements from the childs of the canvas
      var elements = this.facade
        .getCanvas()
        .getChildShapes(true)
        .findAll(function (value) {
          var absBounds = value.absoluteBounds();

          var bA = absBounds.upperLeft();
          var bB = absBounds.lowerRight();

          if (bA.x > a.x && bA.y > a.y && bB.x < b.x && bB.y < b.y) return true;
          return false;
        });

      // Set the selection
      this.facade.setSelection(elements);
    }
  },

  handleMouseMove: function (event) {
    // Calculate the size
    var size = {
      width: Event.pointerX(event) - this.position.x - jQuery('#canvasSection').offset().left,
      height: Event.pointerY(event) - this.position.y - jQuery('#canvasSection').offset().top + 5,
    };

    var scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode;
    size.width -= this.offsetScroll.x - scrollNode.scrollLeft;
    size.height -= this.offsetScroll.y - scrollNode.scrollTop;

    // Set the size
    this.resize(size);

    Event.stop(event);
  },

  hide: function () {
    this.node.style.display = 'none';
  },

  show: function () {
    this.node.style.display = '';
  },

  setPos: function (pos) {
    // Set the Position
    this.node.style.top = pos.y + 'px';
    this.node.style.left = pos.x + 'px';
    this.position = pos;
  },

  resize: function (size) {
    // Calculate the negative offset
    this.setPos(this.position);
    this.size = Object.clone(size);

    if (size.width < 0) {
      this.node.style.left = this.position.x + size.width + 'px';
      size.width = -size.width;
    }
    if (size.height < 0) {
      this.node.style.top = this.position.y + size.height + 'px';
      size.height = -size.height;
    }

    // Set the size
    this.node.style.width = size.width + 'px';
    this.node.style.height = size.height + 'px';
  },
});

/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.ShapeHighlighting = Clazz.extend({
  construct: function (facade) {
    this.parentNode = facade.getCanvas().getSvgContainer();

    // The parent Node
    this.node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.parentNode, ['g']);

    this.highlightNodes = {};

    facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, this.setHighlight.bind(this));
    facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, this.hideHighlight.bind(this));
  },

  setHighlight: function (options) {
    if (options && options.highlightId) {
      var node = this.highlightNodes[options.highlightId];

      if (!node) {
        node = ORYX.Editor.graft('http://www.w3.org/2000/svg', this.node, [
          'path',
          {
            'stroke-width': 2.0,
            fill: 'none',
          },
        ]);

        this.highlightNodes[options.highlightId] = node;
      }

      if (options.elements && options.elements.length > 0) {
        this.setAttributesByStyle(node, options);
        this.show(node);
      } else {
        this.hide(node);
      }
    }
  },

  hideHighlight: function (options) {
    if (options && options.highlightId && this.highlightNodes[options.highlightId]) {
      this.hide(this.highlightNodes[options.highlightId]);
    }
  },

  hide: function (node) {
    node.setAttributeNS(null, 'display', 'none');
  },

  show: function (node) {
    node.setAttributeNS(null, 'display', '');
  },

  setAttributesByStyle: function (node, options) {
    // If the style say, that it should look like a rectangle
    if (options.style && options.style == ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE) {
      // Set like this
      var bo = options.elements[0].absoluteBounds();

      var strWidth = options.strokewidth ? options.strokewidth : ORYX.CONFIG.BORDER_OFFSET;

      node.setAttributeNS(null, 'd', this.getPathRectangle(bo.a, bo.b, strWidth));
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR);
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 0.2);
      node.setAttributeNS(null, 'stroke-width', strWidth);
    } else if (options.elements.length == 1 && options.elements[0] instanceof ORYX.Core.Edge && options.highlightId != 'selection') {
      /* Highlight containment of edge's childs */
      var path = this.getPathEdge(options.elements[0].dockers);
      if (path && path.length > 0) {
        node.setAttributeNS(null, 'd', path);
      }
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR);
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 0.2);
      node.setAttributeNS(null, 'stroke-width', ORYX.CONFIG.OFFSET_EDGE_BOUNDS);
    } else {
      // If not, set just the corners
      var path = this.getPathByElements(options.elements);
      if (path && path.length > 0) {
        node.setAttributeNS(null, 'd', path);
      }
      node.setAttributeNS(null, 'stroke', options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR);
      node.setAttributeNS(null, 'stroke-opacity', options.opacity ? options.opacity : 1.0);
      node.setAttributeNS(null, 'stroke-width', options.strokewidth ? options.strokewidth : 2.0);
    }
  },

  getPathByElements: function (elements) {
    if (!elements || elements.length <= 0) {
      return undefined;
    }

    // Get the padding and the size
    var padding = ORYX.CONFIG.SELECTED_AREA_PADDING;

    var path = '';

    // Get thru all Elements
    elements.each(
      function (element) {
        if (!element) {
          return;
        }
        // Get the absolute Bounds and the two Points
        var bounds = element.absoluteBounds();
        bounds.widen(padding);
        var a = bounds.upperLeft();
        var b = bounds.lowerRight();

        path = path + this.getPath(a, b);
      }.bind(this)
    );

    return path;
  },

  getPath: function (a, b) {
    return this.getPathCorners(a, b);
  },

  getPathCorners: function (a, b) {
    var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE;

    var path = '';

    // Set: Upper left
    path = path + 'M' + a.x + ' ' + (a.y + size) + ' l0 -' + size + ' l' + size + ' 0 ';
    // Set: Lower left
    path = path + 'M' + a.x + ' ' + (b.y - size) + ' l0 ' + size + ' l' + size + ' 0 ';
    // Set: Lower right
    path = path + 'M' + b.x + ' ' + (b.y - size) + ' l0 ' + size + ' l-' + size + ' 0 ';
    // Set: Upper right
    path = path + 'M' + b.x + ' ' + (a.y + size) + ' l0 -' + size + ' l-' + size + ' 0 ';

    return path;
  },

  getPathRectangle: function (a, b, strokeWidth) {
    var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE;

    var path = '';
    var offset = strokeWidth / 2.0;

    // Set: Upper left
    path = path + 'M' + (a.x + offset) + ' ' + a.y;
    path = path + ' L' + (a.x + offset) + ' ' + (b.y - offset);
    path = path + ' L' + (b.x - offset) + ' ' + (b.y - offset);
    path = path + ' L' + (b.x - offset) + ' ' + (a.y + offset);
    path = path + ' L' + (a.x + offset) + ' ' + (a.y + offset);

    return path;
  },

  getPathEdge: function (edgeDockers) {
    var length = edgeDockers.length;
    var path = 'M' + edgeDockers[0].bounds.center().x + ' ' + edgeDockers[0].bounds.center().y;

    for (i = 1; i < length; i++) {
      var dockerPoint = edgeDockers[i].bounds.center();
      path = path + ' L' + dockerPoint.x + ' ' + dockerPoint.y;
    }

    return path;
  },
});

ORYX.Plugins.HighlightingSelectedShapes = Clazz.extend({
  construct: function (facade) {
    this.facade = facade;
    this.opacityFull = 0.9;
    this.opacityLow = 0.4;

    // Register on Dragging-Events for show/hide of ShapeMenu
    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hide.bind(this));
    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END,  this.show.bind(this));
  },

  /**
   * On the Selection-Changed
   *
   */
  onSelectionChanged: function (event) {
    if (event.elements && event.elements.length > 1) {
      this.facade.raiseEvent({
        type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
        highlightId: 'selection',
        elements: event.elements.without(event.subSelection),
        color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
        opacity: !event.subSelection ? this.opacityFull : this.opacityLow,
      });

      if (event.subSelection) {
        this.facade.raiseEvent({
          type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
          highlightId: 'subselection',
          elements: [event.subSelection],
          color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
          opacity: this.opacityFull,
        });
      } else {
        this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'subselection' });
      }
    } else {
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'selection' });
      this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId: 'subselection' });
    }
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.Overlay = Clazz.extend({
  facade: undefined,

  styleNode: undefined,

  construct: function (facade) {
    this.facade = facade;

    this.changes = [];

    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_SHOW, this.show.bind(this));
    this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_HIDE, this.hide.bind(this));

    this.styleNode = document.createElement('style');
    this.styleNode.setAttributeNS(null, 'type', 'text/css');

    document.getElementsByTagName('head')[0].appendChild(this.styleNode);
  },

  /**
   * Show the overlay for specific nodes
   * @param {Object} options
   *
   * 	String				options.id		- MUST - Define the id of the overlay (is needed for the hiding of this overlay)
   *	ORYX.Core.Shape[] 	options.shapes 	- MUST - Define the Shapes for the changes
   * 	attr-name:value		options.changes	- Defines all the changes which should be shown
   *
   *
   */
  show: function (options) {
    // Checks if all arguments are available
    if (
      !options ||
      !options.shapes ||
      !options.shapes instanceof Array ||
      !options.id ||
      !options.id instanceof String ||
      options.id.length == 0
    ) {
      return;
    }

    //if( this.changes[options.id]){
    //	this.hide( options )
    //}

    // Checked if attributes are setted
    if (options.attributes) {
      // FOR EACH - Shape
      options.shapes.each(
        function (el) {
          // Checks if the node is a Shape
          if (!el instanceof ORYX.Core.Shape) {
            return;
          }

          this.setAttributes(el.node, options.attributes);
        }.bind(this)
      );
    }

    var isSVG = true;
    try {
      isSVG = options.node && options.node instanceof SVGElement;
    } catch (e) {}

    // Checks if node is setted and if this is an SVGElement
    if (options.node && isSVG) {
      options['_temps'] = [];

      // FOR EACH - Node
      options.shapes.each(
        function (el, index) {
          // Checks if the node is a Shape
          if (!el instanceof ORYX.Core.Shape) {
            return;
          }

          var _temp = {};
          _temp.svg = options.dontCloneNode ? options.node : options.node.cloneNode(true);

          // Add the svg node to the ORYX-Shape
          el.node.firstChild.appendChild(_temp.svg);

          // If
          if (el instanceof ORYX.Core.Edge && !options.nodePosition) {
            options['nodePosition'] = 'START';
          }

          // If the node position is setted, it has to be transformed
          if (options.nodePosition) {
            var b = el.bounds;
            var p = options.nodePosition.toUpperCase();

            // Check the values of START and END
            if (el instanceof ORYX.Core.Node && p == 'START') {
              p = 'NW';
            } else if (el instanceof ORYX.Core.Node && p == 'END') {
              p = 'SE';
            } else if (el instanceof ORYX.Core.Edge && p == 'START') {
              b = el.getDockers().first().bounds;
            } else if (el instanceof ORYX.Core.Edge && p == 'END') {
              b = el.getDockers().last().bounds;
            }

            // Create a callback for the changing the position
            // depending on the position string
            _temp.callback = function () {
              var x = 0;
              var y = 0;

              if (p == 'NW') {
                // Do Nothing
              } else if (p == 'N') {
                x = b.width() / 2;
              } else if (p == 'NE') {
                x = b.width();
              } else if (p == 'E') {
                x = b.width();
                y = b.height() / 2;
              } else if (p == 'SE') {
                x = b.width();
                y = b.height();
              } else if (p == 'S') {
                x = b.width() / 2;
                y = b.height();
              } else if (p == 'SW') {
                y = b.height();
              } else if (p == 'W') {
                y = b.height() / 2;
              } else if (p == 'START' || p == 'END') {
                x = b.width() / 2;
                y = b.height() / 2;
              } else {
                return;
              }

              if (el instanceof ORYX.Core.Edge) {
                x += b.upperLeft().x;
                y += b.upperLeft().y;
              }

              _temp.svg.setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')');
            }.bind(this);

            _temp.element = el;
            _temp.callback();

            b.registerCallback(_temp.callback);
          }

          options._temps.push(_temp);
        }.bind(this)
      );
    }

    // Store the changes
    if (!this.changes[options.id]) {
      this.changes[options.id] = [];
    }

    this.changes[options.id].push(options);
  },

  /**
   * Hide the overlay with the spefic id
   * @param {Object} options
   */
  hide: function (options) {
    // Checks if all arguments are available
    if (!options || !options.id || !options.id instanceof String || options.id.length == 0 || !this.changes[options.id]) {
      return;
    }

    // Delete all added attributes
    // FOR EACH - Shape
    this.changes[options.id].each(
      function (option) {
        option.shapes.each(
          function (el, index) {
            // Checks if the node is a Shape
            if (!el instanceof ORYX.Core.Shape) {
              return;
            }

            this.deleteAttributes(el.node);
          }.bind(this)
        );

        if (option._temps) {
          option._temps.each(
            function (tmp) {
              // Delete the added Node, if there is one
              if (tmp.svg && tmp.svg.parentNode) {
                tmp.svg.parentNode.removeChild(tmp.svg);
              }

              // If
              if (tmp.callback && tmp.element) {
                // It has to be unregistered from the edge
                tmp.element.bounds.unregisterCallback(tmp.callback);
              }
            }.bind(this)
          );
        }
      }.bind(this)
    );

    this.changes[options.id] = null;
  },

  /**
   * Set the given css attributes to that node
   * @param {HTMLElement} node
   * @param {Object} attributes
   */
  setAttributes: function (node, attributes) {
    // Get all the childs from ME
    var childs = this.getAllChilds(node.firstChild.firstChild);

    var ids = [];

    // Add all Attributes which have relation to another node in this document and concate the pure id out of it
    // This is for example important for the markers of a edge
    childs.each(function (e) {
      ids.push(
        $A(e.attributes).findAll(function (attr) {
          return attr.nodeValue.startsWith('url(#');
        })
      );
    });
    ids = ids.flatten().compact();
    ids = ids
      .collect(function (s) {
        return s.nodeValue;
      })
      .uniq();
    ids = ids.collect(function (s) {
      return s.slice(5, s.length - 1);
    });

    // Add the node ID to the id
    ids.unshift(node.id + ' .me');

    var attr = $H(attributes);
    var attrValue = attr.toJSON().gsub(',', ';').gsub('"', '');
    var attrMarkerValue = attributes.stroke ? attrValue.slice(0, attrValue.length - 1) + '; fill:' + attributes.stroke + ';}' : attrValue;
    var attrTextValue;
    if (attributes.fill) {
      var copyAttr = Object.clone(attributes);
      copyAttr.fill = 'black';
      attrTextValue = $H(copyAttr).toJSON().gsub(',', ';').gsub('"', '');
    }

    // Create the CSS-Tags Style out of the ids and the attributes
    csstags = ids.collect(function (s, i) {
      return '#' + s + ' * ' + (!i ? attrValue : attrMarkerValue) + '' + (attrTextValue ? ' #' + s + ' text * ' + attrTextValue : '');
    });

    // Join all the tags
    var s = csstags.join(' ') + '\n';

    // And add to the end of the style tag
    this.styleNode.appendChild(document.createTextNode(s));
  },

  /**
   * Deletes all attributes which are
   * added in a special style sheet for that node
   * @param {HTMLElement} node
   */
  deleteAttributes: function (node) {
    // Get all children which contains the node id
    var delEl = $A(this.styleNode.childNodes).findAll(function (e) {
      return e.textContent.include('#' + node.id);
    });

    // Remove all of them
    delEl.each(function (el) {
      el.parentNode.removeChild(el);
    });
  },

  getAllChilds: function (node) {
    var childs = $A(node.childNodes);

    $A(node.childNodes).each(
      function (e) {
        childs.push(this.getAllChilds(e));
      }.bind(this)
    );

    return childs.flatten();
  },
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

ORYX.Plugins.KeysMove = ORYX.Plugins.AbstractPlugin.extend({
  facade: undefined,

  construct: function (facade) {
    this.facade = facade;
    this.copyElements = [];

    //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));

    // SELECT ALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: 65,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.selectAll.bind(this),
    });

    // MOVE LEFT SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, false),
    });

    // MOVE LEFT
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, true),
    });

    // MOVE RIGHT SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, false),
    });

    // MOVE RIGHT
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, true),
    });

    // MOVE UP SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_UP,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, false),
    });

    // MOVE UP
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_UP,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, true),
    });

    // MOVE DOWN SMALL
    this.facade.offer({
      keyCodes: [
        {
          metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
          keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, false),
    });

    // MOVE DOWN
    this.facade.offer({
      keyCodes: [
        {
          keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
          keyAction: ORYX.CONFIG.KEY_ACTION_DOWN,
        },
      ],
      functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, true),
    });
  },

  /**
   * Select all shapes in the editor
   *
   */
  selectAll: function (e) {
    Event.stop(e.event);
    this.facade.setSelection(this.facade.getCanvas().getChildShapes(true));
  },

  move: function (key, far, e) {
    Event.stop(e.event);

    // calculate the distance to move the objects and get the selection.
    var distance = far ? 20 : 5;
    var selection = this.facade.getSelection();
    var currentSelection = this.facade.getSelection();
    var p = { x: 0, y: 0 };

    // switch on the key pressed and populate the point to move by.
    switch (key) {
      case ORYX.CONFIG.KEY_CODE_LEFT:
        p.x = -1 * distance;
        break;
      case ORYX.CONFIG.KEY_CODE_RIGHT:
        p.x = distance;
        break;
      case ORYX.CONFIG.KEY_CODE_UP:
        p.y = -1 * distance;
        break;
      case ORYX.CONFIG.KEY_CODE_DOWN:
        p.y = distance;
        break;
    }

    // move each shape in the selection by the point calculated and update it.
    selection = selection.findAll(function (shape) {
      // Check if this shape is docked to an shape in the selection
      if (shape instanceof ORYX.Core.Node && shape.dockers.length == 1 && selection.include(shape.dockers.first().getDockedShape())) {
        return false;
      }

      // Check if any of the parent shape is included in the selection
      var s = shape.parent;
      do {
        if (selection.include(s)) {
          return false;
        }
      } while ((s = s.parent));

      // Otherwise, return true
      return true;
    });

    /* Edges must not be movable, if only edges are selected and at least
     * one of them is docked.
     */
    var edgesMovable = true;
    var onlyEdgesSelected = selection.all(function (shape) {
      if (shape instanceof ORYX.Core.Edge) {
        if (shape.isDocked()) {
          edgesMovable = false;
        }
        return true;
      }
      return false;
    });

    if (onlyEdgesSelected && !edgesMovable) {
      /* Abort moving shapes */
      return;
    }

    selection = selection
      .map(function (shape) {
        if (shape instanceof ORYX.Core.Node) {
          /*if( shape.dockers.length == 1 ){
					return shape.dockers.first()
				} else {*/
          return shape;
          //}
        } else if (shape instanceof ORYX.Core.Edge) {
          var dockers = shape.dockers;

          if (selection.include(shape.dockers.first().getDockedShape())) {
            dockers = dockers.without(shape.dockers.first());
          }

          if (selection.include(shape.dockers.last().getDockedShape())) {
            dockers = dockers.without(shape.dockers.last());
          }

          return dockers;
        } else {
          return null;
        }
      })
      .flatten()
      .compact();

    if (selection.size() > 0) {
      //Stop moving at canvas borders
      var selectionBounds = [this.facade.getCanvas().bounds.lowerRight().x, this.facade.getCanvas().bounds.lowerRight().y, 0, 0];
      selection.each(function (s) {
        selectionBounds[0] = Math.min(selectionBounds[0], s.bounds.upperLeft().x);
        selectionBounds[1] = Math.min(selectionBounds[1], s.bounds.upperLeft().y);
        selectionBounds[2] = Math.max(selectionBounds[2], s.bounds.lowerRight().x);
        selectionBounds[3] = Math.max(selectionBounds[3], s.bounds.lowerRight().y);
      });
      if (selectionBounds[0] + p.x < 0) p.x = -selectionBounds[0];
      if (selectionBounds[1] + p.y < 0) p.y = -selectionBounds[1];
      if (selectionBounds[2] + p.x > this.facade.getCanvas().bounds.lowerRight().x)
        p.x = this.facade.getCanvas().bounds.lowerRight().x - selectionBounds[2];
      if (selectionBounds[3] + p.y > this.facade.getCanvas().bounds.lowerRight().y)
        p.y = this.facade.getCanvas().bounds.lowerRight().y - selectionBounds[3];

      if (p.x != 0 || p.y != 0) {
        // Instantiate the moveCommand
        var commands = [new ORYX.Core.Command.Move(selection, p, null, currentSelection, this)];
        // Execute the commands
        this.facade.executeCommands(commands);
      }
    }
  },

  getUndockedCommant: function (shapes) {
    var undockEdgeCommand = ORYX.Core.Command.extend({
      construct: function (moveShapes) {
        this.dockers = moveShapes
          .collect(function (shape) {
            return shape instanceof ORYX.Core.Controls.Docker
              ? { docker: shape, dockedShape: shape.getDockedShape(), refPoint: shape.referencePoint }
              : undefined;
          })
          .compact();
      },
      execute: function () {
        this.dockers.each(function (el) {
          el.docker.setDockedShape(undefined);
        });
      },
      rollback: function () {
        this.dockers.each(function (el) {
          el.docker.setDockedShape(el.dockedShape);
          el.docker.setReferencePoint(el.refPoint);
          //el.docker.update();
        });
      },
    });

    command = new undockEdgeCommand(shapes);
    command.execute();
    return command;
  },

  //    /**
  //     * The key handler for this plugin. Every action from the set of cut, copy,
  //     * paste and delete should be accessible trough simple keyboard shortcuts.
  //     * This method checks whether any event triggers one of those actions.
  //     *
  //     * @param {Object} event The keyboard event that should be analysed for
  //     *     triggering of this plugin.
  //     */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //
  //        ORYX.Log.debug("keysMove.js handles a keyEvent.");
  //
  //        // assure we have the current event.
  //        if (!event)
  //            event = window.event;
  //
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //		// if the key is one of the arrow keys, forward to move and return.
  //		if ([ORYX.CONFIG.KEY_CODE_LEFT, ORYX.CONFIG.KEY_CODE_RIGHT,
  //			ORYX.CONFIG.KEY_CODE_UP, ORYX.CONFIG.KEY_CODE_DOWN].include(pressedKey)) {
  //
  //			this.move(pressedKey, !ctrlPressed);
  //			return;
  //		}
  //
  //    }
});
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) {
  ORYX.Plugins = {};
}
if (!ORYX.Plugins.Layouter) {
  ORYX.Plugins.Layouter = {};
}

new (function () {
  /**
   * Edge layouter is an implementation to layout an edge
   * @class ORYX.Plugins.Layouter.EdgeLayouter
   * @author Willi Tscheschner
   */
  ORYX.Plugins.Layouter.EdgeLayouter = ORYX.Plugins.AbstractLayouter.extend({
    /**
     * Layout only Edges
     */
    layouted: [
      'http://b3mn.org/stencilset/bpmn1.1#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn1.1#MessageFlow',
      'http://b3mn.org/stencilset/timjpdl3#SequenceFlow',
      'http://b3mn.org/stencilset/jbpm4#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0#MessageFlow',
      'http://b3mn.org/stencilset/bpmn2.0#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0choreography#MessageFlow',
      'http://b3mn.org/stencilset/bpmn2.0choreography#SequenceFlow',
      'http://b3mn.org/stencilset/bpmn2.0conversation#ConversationLink',
      'http://b3mn.org/stencilset/epc#ControlFlow',
      'http://www.signavio.com/stencilsets/processmap#ProcessLink',
      'http://www.signavio.com/stencilsets/organigram#connection',
    ],

    /**
     * Layout a set on edges
     * @param {Object} edges
     */
    layout: function (edges) {
      edges.each(
        function (edge) {
          this.doLayout(edge);
        }.bind(this)
      );
    },

    /**
     * Layout one edge
     * @param {Object} edge
     */
    doLayout: function (edge) {
      // Get from and to node
      var from = edge.getIncomingNodes()[0];
      var to = edge.getOutgoingNodes()[0];

      // Return if one is null
      if (!from || !to) {
        return;
      }

      var positions = this.getPositions(from, to, edge);

      if (positions.length > 0) {
        this.setDockers(edge, positions[0].a, positions[0].b);
      }
    },

    /**
     * Returns a set on positions which are not containt either
     * in the bounds in from or to.
     * @param {Object} from Shape where the edge is come from
     * @param {Object} to Shape where the edge is leading to
     * @param {Object} edge Edge between from and to
     */
    getPositions: function (from, to, edge) {
      // Get absolute bounds
      var ab = from.absoluteBounds();
      var bb = to.absoluteBounds();

      // Get center from and to
      var a = ab.center();
      var b = bb.center();

      var am = ab.midPoint();
      var bm = bb.midPoint();

      // Get first and last reference point
      var first = Object.clone(edge.dockers.first().referencePoint);
      var last = Object.clone(edge.dockers.last().referencePoint);
      // Get the absolute one
      var aFirst = edge.dockers.first().getAbsoluteReferencePoint();
      var aLast = edge.dockers.last().getAbsoluteReferencePoint();

      // IF ------>
      // or  |
      //     V
      // Do nothing
      if (Math.abs(aFirst.x - aLast.x) < 1 || Math.abs(aFirst.y - aLast.y) < 1) {
        return [];
      }

      // Calc center position, between a and b
      // depending on there weight
      var m = {};
      m.x =
        a.x < b.x
          ? (b.x - bb.width() / 2 - (a.x + ab.width() / 2)) / 2 + (a.x + ab.width() / 2)
          : (a.x - ab.width() / 2 - (b.x + bb.width() / 2)) / 2 + (b.x + bb.width() / 2);

      m.y =
        a.y < b.y
          ? (b.y - bb.height() / 2 - (a.y + ab.height() / 2)) / 2 + (a.y + ab.height() / 2)
          : (a.y - ab.height() / 2 - (b.y + bb.height() / 2)) / 2 + (b.y + bb.height() / 2);

      // Enlarge both bounds with 10
      ab.widen(5); // Wide the from less than
      bb.widen(20); // the to because of the arrow from the edge

      var positions = [];
      var off = this.getOffset.bind(this);

      // Checks ----+
      //            |
      //            V
      if (!ab.isIncluded(b.x, a.y) && !bb.isIncluded(b.x, a.y)) {
        positions.push({
          a: { x: b.x + off(last, bm, 'x'), y: a.y + off(first, am, 'y') },
          z: this.getWeight(from, a.x < b.x ? 'r' : 'l', to, a.y < b.y ? 't' : 'b', edge),
        });
      }

      // Checks |
      //        +--->
      if (!ab.isIncluded(a.x, b.y) && !bb.isIncluded(a.x, b.y)) {
        positions.push({
          a: { x: a.x + off(first, am, 'x'), y: b.y + off(last, bm, 'y') },
          z: this.getWeight(from, a.y < b.y ? 'b' : 't', to, a.x < b.x ? 'l' : 'r', edge),
        });
      }

      // Checks  --+
      //           |
      //           +--->
      if (!ab.isIncluded(m.x, a.y) && !bb.isIncluded(m.x, b.y)) {
        positions.push({
          a: { x: m.x, y: a.y + off(first, am, 'y') },
          b: { x: m.x, y: b.y + off(last, bm, 'y') },
          z: this.getWeight(from, 'r', to, 'l', edge, a.x > b.x),
        });
      }

      // Checks |
      //        +---+
      //            |
      //            V
      if (!ab.isIncluded(a.x, m.y) && !bb.isIncluded(b.x, m.y)) {
        positions.push({
          a: { x: a.x + off(first, am, 'x'), y: m.y },
          b: { x: b.x + off(last, bm, 'x'), y: m.y },
          z: this.getWeight(from, 'b', to, 't', edge, a.y > b.y),
        });
      }

      // Sort DESC of weights
      return positions.sort(function (a, b) {
        return a.z < b.z ? 1 : a.z == b.z ? -1 : -1;
      });
    },

    /**
     * Returns a offset for the pos to the center of the bounds
     *
     * @param {Object} val
     * @param {Object} pos2
     * @param {String} dir Direction x|y
     */
    getOffset: function (pos, pos2, dir) {
      return pos[dir] - pos2[dir];
    },

    /**
     * Returns a value which shows the weight for this configuration
     *
     * @param {Object} from Shape which is coming from
     * @param {String} d1 Direction where is goes
     * @param {Object} to Shape which goes to
     * @param {String} d2 Direction where it comes to
     * @param {Object} edge Edge between from and to
     * @param {Boolean} reverse Reverse the direction (e.g. "r" -> "l")
     */
    getWeight: function (from, d1, to, d2, edge, reverse) {
      d1 = (d1 || '').toLowerCase();
      d2 = (d2 || '').toLowerCase();

      if (!['t', 'r', 'b', 'l'].include(d1)) {
        d1 = 'r';
      }
      if (!['t', 'r', 'b', 'l'].include(d2)) {
        d1 = 'l';
      }

      // If reverse is set
      if (reverse) {
        // Reverse d1 and d2
        d1 = d1 == 't' ? 'b' : d1 == 'r' ? 'l' : d1 == 'b' ? 't' : d1 == 'l' ? 'r' : 'r';
        d2 = d2 == 't' ? 'b' : d2 == 'r' ? 'l' : d2 == 'b' ? 't' : d2 == 'l' ? 'r' : 'r';
      }

      var weight = 0;
      // Get rules for from "out" and to "in"
      var dr1 = this.facade.getRules().getLayoutingRules(from, edge)['out'];
      var dr2 = this.facade.getRules().getLayoutingRules(to, edge)['in'];

      var fromWeight = dr1[d1];
      var toWeight = dr2[d2];

      /**
       * Return a true if the center 1 is in the same direction than center 2
       * @param {Object} direction
       * @param {Object} center1
       * @param {Object} center2
       */
      var sameDirection = function (direction, center1, center2) {
        switch (direction) {
          case 't':
            return Math.abs(center1.x - center2.x) < 2 && center1.y < center2.y;
          case 'r':
            return center1.x > center2.x && Math.abs(center1.y - center2.y) < 2;
          case 'b':
            return Math.abs(center1.x - center2.x) < 2 && center1.y > center2.y;
          case 'l':
            return center1.x < center2.x && Math.abs(center1.y - center2.y) < 2;
          default:
            return false;
        }
      };

      // Check if there are same incoming edges from 'from'
      var sameIncomingFrom = from
        .getIncomingShapes()
        .findAll(function (a) {
          return a instanceof ORYX.Core.Edge;
        })
        .any(function (e) {
          return sameDirection(d1, e.dockers[e.dockers.length - 2].bounds.center(), e.dockers.last().bounds.center());
        });

      // Check if there are same outgoing edges from 'to'
      var sameOutgoingTo = to
        .getOutgoingShapes()
        .findAll(function (a) {
          return a instanceof ORYX.Core.Edge;
        })
        .any(function (e) {
          return sameDirection(d2, e.dockers[1].bounds.center(), e.dockers.first().bounds.center());
        });

      // If there are equivalent edges, set 0
      //fromWeight = sameIncomingFrom ? 0 : fromWeight;
      //toWeight = sameOutgoingTo ? 0 : toWeight;

      // Get the sum of "out" and the direction plus "in" and the direction
      return sameIncomingFrom || sameOutgoingTo ? 0 : fromWeight + toWeight;
    },

    /**
     * Removes all current dockers from the node
     * (except the start and end) and adds two new
     * dockers, on the position a and b.
     * @param {Object} edge
     * @param {Object} a
     * @param {Object} b
     */
    setDockers: function (edge, a, b) {
      if (!edge) {
        return;
      }

      // Remove all dockers (implicit,
      // start and end dockers will not removed)
      edge.dockers.each(function (r) {
        edge.removeDocker(r);
      });

      // For a and b (if exists), create
      // a new docker and set position
      [a, b].compact().each(function (pos) {
        var docker = edge.createDocker(undefined, pos);
        docker.bounds.centerMoveTo(pos);
      });

      // Update all dockers from the edge
      edge.dockers.each(function (docker) {
        docker.update();
      });

      // Update edge
      //edge.refresh();
      edge._update(true);
    },
  });
})();
/*
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * License rights for this program may be obtained from Alfresco Software, Ltd.
 * pursuant to a written agreement and any use of this program without such an
 * agreement is prohibited.
 */
/*
 * All code Copyright 2013 KIS Consultancy all rights reserved
 */

if (!ORYX.Plugins) ORYX.Plugins = new Object();

new (function () {
  ORYX.Plugins.BPMN2_0 = {
    /**
     *	Constructor
     *	@param {Object} Facade: The Facade of the Editor
     */
    construct: function (facade) {
      this.facade = facade;

      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED, this.handleDockerDocked.bind(this));
      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED, this.handlePropertyChanged.bind(this));
      this.facade.registerOnEvent('layout.bpmn2_0.pool', this.handleLayoutPool.bind(this));
      this.facade.registerOnEvent('layout.bpmn2_0.subprocess', this.handleSubProcess.bind(this));
      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, this.handleShapeRemove.bind(this));

      this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.afterLoad.bind(this));

      this.namespace = undefined;
    },

    /**
     * Force to update every pool
     */
    afterLoad: function () {
      this.facade
        .getCanvas()
        .getChildNodes()
        .each(
          function (shape) {
            if (shape.getStencil().id().endsWith('Pool')) {
              this.handleLayoutPool({
                shape: shape,
              });
            }
          }.bind(this)
        );
    },

    /**
     * If a pool is selected and contains no lane,
     * a lane is created automagically
     */
    onSelectionChanged: function (event) {
      var selection = event.elements;

      if (selection && selection.length === 1) {
        var namespace = this.getNamespace();
        var shape = selection[0];
        if (shape.getStencil().idWithoutNs() === 'Pool') {
          if (shape.getChildNodes().length === 0) {
            // create a lane inside the selected pool
            var option = {
              type: namespace + 'Lane',
              position: { x: 0, y: 0 },
              namespace: shape.getStencil().namespace(),
              parent: shape,
            };
            this.facade.createShape(option);
            this.facade.getCanvas().update();
            this.facade.setSelection([shape]);
          }
        }
      }

      // Preventing selection of all lanes but not the pool
      if (
        selection.any(function (s) {
          return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith('Lane');
        })
      ) {
        var lanes = selection.findAll(function (s) {
          return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith('Lane');
        });

        var pools = [];
        var unselectLanes = [];
        lanes.each(
          function (lane) {
            pools.push(this.getParentPool(lane));
          }.bind(this)
        );

        pools = pools.uniq().findAll(
          function (pool) {
            var childLanes = this.getLanes(pool, true);
            if (
              childLanes.all(function (lane) {
                return lanes.include(lane);
              })
            ) {
              unselectLanes = unselectLanes.concat(childLanes);
              return true;
            } else if (
              selection.include(pool) &&
              childLanes.any(function (lane) {
                return lanes.include(lane);
              })
            ) {
              unselectLanes = unselectLanes.concat(childLanes);
              return true;
            } else {
              return false;
            }
          }.bind(this)
        );

        if (unselectLanes.length > 0 && pools.length > 0) {
          selection = selection.without.apply(selection, unselectLanes);
          selection = selection.concat(pools);
          this.facade.setSelection(selection.uniq());
        }
      }
    },

    handleShapeRemove: function (option) {
      var sh = option.shape;
      var parent = option.parent;

      if (sh instanceof ORYX.Core.Node && sh.getStencil().idWithoutNs() === 'Lane' && this.facade.isExecutingCommands()) {
        var pool = this.getParentPool(parent);
        if (pool && pool.parent) {
          var isLeafFn = function (leaf) {
            return !leaf.getChildNodes().any(function (r) {
              return r.getStencil().idWithoutNs() === 'Lane';
            });
          };

          var isLeaf = isLeafFn(sh);
          var parentHasMoreLanes = parent.getChildNodes().any(function (r) {
            return r.getStencil().idWithoutNs() === 'Lane';
          });

          if (isLeaf && parentHasMoreLanes) {
            var command = new ResizeLanesCommand(sh, parent, pool, this);
            this.facade.executeCommands([command]);
          } else if (
            !isLeaf &&
            !this.facade.getSelection().any(function (select) {
              // Find one of the selection, which is a lane and child of "sh" and is a leaf lane
              return (
                select instanceof ORYX.Core.Node && select.getStencil().idWithoutNs() === 'Lane' && select.isParent(sh) && isLeafFn(select)
              );
            })
          ) {
            var Command = ORYX.Core.Command.extend({
              construct: function (shape, facade) {
                this.children = shape.getChildNodes(true);
                this.facade = facade;
              },
              execute: function () {
                this.children.each(function (child) {
                  child.bounds.moveBy(30, 0);
                });
                //this.facade.getCanvas().update();
              },
              rollback: function () {
                this.children.each(function (child) {
                  child.bounds.moveBy(-30, 0);
                });
                //this.facade.getCanvas().update();
              },
            });
            this.facade.executeCommands([new Command(sh, this.facade)]);
          } else if (isLeaf && !parentHasMoreLanes && parent == pool) {
            parent.add(sh);
          }
        }
      }
    },

    hashedSubProcesses: {},

    hashChildShapes: function (shape) {
      var children = shape.getChildNodes();
      children.each(
        function (child) {
          if (this.hashedSubProcesses[child.id]) {
            this.hashedSubProcesses[child.id] = child.absoluteXY();
            this.hashedSubProcesses[child.id].width = child.bounds.width();
            this.hashedSubProcesses[child.id].height = child.bounds.height();
            this.hashChildShapes(child);
          }
        }.bind(this)
      );
    },

    /**
     * Handle the layouting of a sub process.
     * Mainly to adjust the child dockers of a sub process.
     *
     */
    handleSubProcess: function (option) {
      var sh = option.shape;

      if (!this.hashedSubProcesses[sh.id]) {
        this.hashedSubProcesses[sh.id] = sh.absoluteXY();
        this.hashedSubProcesses[sh.id].width = sh.bounds.width();
        this.hashedSubProcesses[sh.id].height = sh.bounds.height();
        return;
      }

      var offset = sh.absoluteXY();
      offset.x -= this.hashedSubProcesses[sh.id].x;
      offset.y -= this.hashedSubProcesses[sh.id].y;

      var resized =
        this.hashedSubProcesses[sh.id].width !== sh.bounds.width() || this.hashedSubProcesses[sh.id].height !== sh.bounds.height();

      this.hashedSubProcesses[sh.id] = sh.absoluteXY();
      this.hashedSubProcesses[sh.id].width = sh.bounds.width();
      this.hashedSubProcesses[sh.id].height = sh.bounds.height();
      this.hashChildShapes(sh);

      // Move dockers only if currently is not resizing
      if (this.facade.isExecutingCommands() && !resized) {
        this.moveChildDockers(sh, offset);
      }
    },

    moveChildDockers: function (shape, offset) {
      if (!offset.x && !offset.y) {
        return;
      }

      var children = shape.getChildNodes(true);

      // Get all nodes
      var dockers = children
        // Get all incoming and outgoing edges
        .map(function (node) {
          return [].concat(node.getIncomingShapes()).concat(node.getOutgoingShapes());
        })
        // Flatten all including arrays into one
        .flatten()
        // Get every edge only once
        .uniq()
        // Get all dockers
        .map(function (edge) {
          return edge.dockers.length > 2 ? edge.dockers.slice(1, edge.dockers.length - 1) : [];
        })
        // Flatten the dockers lists
        .flatten();

      var abs = shape.absoluteBounds();
      abs.moveBy(-offset.x, -offset.y);
      var obj = {};
      dockers.each(function (docker) {
        if (docker.isChanged) {
          return;
        }

        var off = Object.clone(offset);

        if (!abs.isIncluded(docker.bounds.center())) {
          var index = docker.parent.dockers.indexOf(docker);
          var size = docker.parent.dockers.length;
          var from = docker.parent.getSource();
          var to = docker.parent.getTarget();

          var bothAreIncluded = children.include(from) && children.include(to);

          if (!bothAreIncluded) {
            var previousIsOver = index !== 0 ? abs.isIncluded(docker.parent.dockers[index - 1].bounds.center()) : false;
            var nextIsOver = index !== size - 1 ? abs.isIncluded(docker.parent.dockers[index + 1].bounds.center()) : false;

            if (!previousIsOver && !nextIsOver) {
              return;
            }

            var ref = docker.parent.dockers[previousIsOver ? index - 1 : index + 1];
            if (Math.abs(-Math.abs(ref.bounds.center().x - docker.bounds.center().x)) < 2) {
              off.y = 0;
            } else if (Math.abs(-Math.abs(ref.bounds.center().y - docker.bounds.center().y)) < 2) {
              off.x = 0;
            } else {
              return;
            }
          }
        }

        obj[docker.getId()] = {
          docker: docker,
          offset: off,
        };
      });

      // Set dockers
      this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(obj)]);
    },

    /**
     * DragDocker.Docked Handler
     *
     */
    handleDockerDocked: function (options) {
      var namespace = this.getNamespace();

      var edge = options.parent;
      var edgeSource = options.target;

      if (edge.getStencil().id() === namespace + 'SequenceFlow') {
        var isGateway = edgeSource
          .getStencil()
          .groups()
          .find(function (group) {
            if (group == 'Gateways') return group;
          });
        if (!isGateway && edge.properties['oryx-conditiontype'] == 'Expression')
          // show diamond on edge source
          edge.setProperty('oryx-showdiamondmarker', true);
        // do not show diamond on edge source
        else edge.setProperty('oryx-showdiamondmarker', false);

        // update edge rendering
        //edge.update();

        this.facade.getCanvas().update();
      }
    },

    /**
     * PropertyWindow.PropertyChanged Handler
     */
    handlePropertyChanged: function (option) {
      var namespace = this.getNamespace();

      var shapes = option.elements;
      var propertyKey = option.key;
      var propertyValue = option.value;

      var changed = false;
      shapes.each(
        function (shape) {
          if (shape.getStencil().id() === namespace + 'SequenceFlow' && propertyKey === 'oryx-conditiontype') {
            if (propertyValue != 'Expression')
              // Do not show the Diamond
              shape.setProperty('oryx-showdiamondmarker', false);
            else {
              var incomingShapes = shape.getIncomingShapes();

              if (!incomingShapes) {
                shape.setProperty('oryx-showdiamondmarker', true);
              }

              var incomingGateway = incomingShapes.find(function (aShape) {
                var foundGateway = aShape
                  .getStencil()
                  .groups()
                  .find(function (group) {
                    if (group == 'Gateways') return group;
                  });
                if (foundGateway) return foundGateway;
              });

              if (!incomingGateway)
                // show diamond on edge source
                shape.setProperty('oryx-showdiamondmarker', true);
              // do not show diamond
              else shape.setProperty('oryx-showdiamondmarker', false);
            }

            changed = true;
          }
        }.bind(this)
      );

      if (changed) {
        this.facade.getCanvas().update();
      }
    },

    hashedPoolPositions: {},
    hashedLaneDepth: {},
    hashedBounds: {},
    hashedPositions: {},

    /**
     * Handler for layouting event 'layout.bpmn2_0.pool'
     * @param {Object} event
     */
    handleLayoutPool: function (event) {
      var pool = event.shape;
      var selection = this.facade.getSelection();
      var currentShape = selection.include(pool) ? pool : selection.first();

      currentShape = currentShape || pool;

      this.currentPool = pool;

      // Check if it is a pool or a lane
      if (!(currentShape.getStencil().id().endsWith('Pool') || currentShape.getStencil().id().endsWith('Lane'))) {
        return;
      }

      // Check if the lane is within the pool and is not removed lately
      if (currentShape !== pool && !currentShape.isParent(pool) && !this.hashedBounds[pool.id][currentShape.id]) {
        return;
      }

      if (!this.hashedBounds[pool.id]) {
        this.hashedBounds[pool.id] = {};
      }

      // Find all child lanes
      var lanes = this.getLanes(pool);

      if (lanes.length <= 0) {
        return;
      }

      var allLanes = this.getLanes(pool, true),
        hp;
      var considerForDockers = allLanes.clone();

      var hashedPositions = $H({});
      allLanes.each(function (lane) {
        hashedPositions[lane.id] = lane.bounds.upperLeft();
      });

      // Show/hide caption regarding the number of lanes
      if (lanes.length === 1 && this.getLanes(lanes.first()).length <= 0) {
        // TRUE if there is a caption
        lanes.first().setProperty('oryx-showcaption', lanes.first().properties['oryx-name'].trim().length > 0);
        var rect = lanes.first().node.getElementsByTagName('rect');
        rect[0].setAttributeNS(null, 'display', 'none');
      } else {
        allLanes.invoke('setProperty', 'oryx-showcaption', true);
        allLanes.each(function (lane) {
          var rect = lane.node.getElementsByTagName('rect');
          rect[0].removeAttributeNS(null, 'display');
        });
      }

      var deletedLanes = [];
      var addedLanes = [];

      // Get all new lanes
      var i = -1;
      while (++i < allLanes.length) {
        if (!this.hashedBounds[pool.id][allLanes[i].id]) {
          addedLanes.push(allLanes[i]);
        }
      }

      if (addedLanes.length > 0) {
        currentShape = addedLanes.first();
      }

      // Get all deleted lanes
      var resourceIds = $H(this.hashedBounds[pool.id]).keys();
      var i = -1;
      while (++i < resourceIds.length) {
        if (
          !allLanes.any(function (lane) {
            return lane.id == resourceIds[i];
          })
        ) {
          deletedLanes.push(this.hashedBounds[pool.id][resourceIds[i]]);
          selection = selection.without(function (r) {
            return r.id == resourceIds[i];
          });
        }
      }

      var height, width, x, y;

      if (deletedLanes.length > 0 || addedLanes.length > 0) {
        if (addedLanes.length === 1 && this.getLanes(addedLanes[0].parent).length === 1) {
          // Set height from the pool
          height = this.adjustHeight(lanes, addedLanes[0].parent);
        } else {
          // Set height from the pool
          height = this.updateHeight(pool);
        }
        // Set width from the pool
        width = this.adjustWidth(lanes, pool.bounds.width());

        pool.update();
      } else if (pool == currentShape) {

      /**
       * Set width/height depending on the pool
       */
        if (selection.length === 1 && this.isResized(pool, this.hashedPoolPositions[pool.id])) {
          var oldXY = this.hashedPoolPositions[pool.id].upperLeft();
          var xy = pool.bounds.upperLeft();
          var scale = 0;
          if (this.shouldScale(pool)) {
            var old = this.hashedPoolPositions[pool.id];
            scale = old.height() / pool.bounds.height();
          }

          this.adjustLanes(pool, allLanes, oldXY.x - xy.x, oldXY.y - xy.y, scale);
        }

        // Set height from the pool
        height = this.adjustHeight(lanes, undefined, pool.bounds.height());
        // Set width from the pool
        width = this.adjustWidth(lanes, pool.bounds.width());
      } else {

      /**???
       * Set width/height depending on containing lanes
       */
        // Reposition the pool if one shape is selected and the upperleft has changed
        if (selection.length === 1 && this.isResized(currentShape, this.hashedBounds[pool.id][currentShape.id])) {
          var oldXY = this.hashedBounds[pool.id][currentShape.id].upperLeft();
          var xy = currentShape.absoluteXY();
          x = oldXY.x - xy.x;
          y = oldXY.y - xy.y;

          // Adjust all other lanes beneath this lane
          if (x || y) {
            considerForDockers = considerForDockers.without(currentShape);
            this.adjustLanes(pool, this.getAllExcludedLanes(pool, currentShape), x, 0);
          }

          // Adjust all child lanes
          var childLanes = this.getLanes(currentShape, true);
          if (childLanes.length > 0) {
            if (this.shouldScale(currentShape)) {
              var old = this.hashedBounds[pool.id][currentShape.id];
              var scale = old.height() / currentShape.bounds.height();
              this.adjustLanes(pool, childLanes, x, y, scale);
            } else {
              this.adjustLanes(pool, childLanes, x, y, 0);
            }
          }
        }

        // Cache all bounds
        var changes = allLanes.map(function (lane) {
          return {
            shape: lane,
            bounds: lane.bounds.clone(),
          };
        });

        // Get height and adjust child heights
        height = this.adjustHeight(lanes, currentShape);
        // Check if something has changed and maybe create a command
        this.checkForChanges(allLanes, changes);

        // Set width from the current shape
        width = this.adjustWidth(lanes, currentShape.bounds.width() + this.getDepth(currentShape, pool) * 30);
      }

      this.setDimensions(pool, width, height, x, y);

      if (this.facade.isExecutingCommands() && (deletedLanes.length === 0 || addedLanes.length !== 0)) {
        // Update all dockers
        this.updateDockers(considerForDockers, pool);

        // Check if the order has changed
        if (
          this.hashedPositions[pool.id] &&
          this.hashedPositions[pool.id].keys().any(function (key, i) {
            return (allLanes[i] || {}).id !== key;
          })
        ) {
          var LanesHasBeenReordered = ORYX.Core.Command.extend({
            construct: function (originPosition, newPosition, lanes, plugin, poolId) {
              this.originPosition = Object.clone(originPosition);
              this.newPosition = Object.clone(newPosition);
              this.lanes = lanes;
              this.plugin = plugin;
              this.pool = poolId;
            },
            execute: function () {
              if (!this.executed) {
                this.executed = true;
                this.lanes.each(
                  function (lane) {
                    if (this.newPosition[lane.id]) lane.bounds.moveTo(this.newPosition[lane.id]);
                  }.bind(this)
                );
                this.plugin.hashedPositions[this.pool] = Object.clone(this.newPosition);
              }
            },
            rollback: function () {
              this.lanes.each(
                function (lane) {
                  if (this.originPosition[lane.id]) lane.bounds.moveTo(this.originPosition[lane.id]);
                }.bind(this)
              );
              this.plugin.hashedPositions[this.pool] = Object.clone(this.originPosition);
            },
          });

          var hp2 = $H({});
          allLanes.each(function (lane) {
            hp2[lane.id] = lane.bounds.upperLeft();
          });

          var command = new LanesHasBeenReordered(hashedPositions, hp2, allLanes, this, pool.id);
          this.facade.executeCommands([command]);
        }
      }

      this.hashedBounds[pool.id] = {};
      this.hashedPositions[pool.id] = hashedPositions;

      var i = -1;
      while (++i < allLanes.length) {
        // Cache positions
        this.hashedBounds[pool.id][allLanes[i].id] = allLanes[i].absoluteBounds();

        // Cache also the bounds of child shapes, mainly for child subprocesses
        this.hashChildShapes(allLanes[i]);

        this.hashedLaneDepth[allLanes[i].id] = this.getDepth(allLanes[i], pool);

        this.forceToUpdateLane(allLanes[i]);
      }

      this.hashedPoolPositions[pool.id] = pool.bounds.clone();

      // Update selection
      //this.facade.setSelection(selection);
    },

    shouldScale: function (element) {
      var childLanes = element.getChildNodes().findAll(function (shape) {
        return shape.getStencil().id().endsWith('Lane');
      });
      return (
        childLanes.length > 1 ||
        childLanes.any(
          function (lane) {
            return this.shouldScale(lane);
          }.bind(this)
        )
      );
    },

    /**
     * Lookup if some bounds has changed
     * @param {Object} lanes
     * @param {Object} changes
     */
    checkForChanges: function (lanes, changes) {
      // Check if something has changed
      if (
        this.facade.isExecutingCommands() &&
        changes.any(function (change) {
          return change.shape.bounds.toString() !== change.bounds.toString();
        })
      ) {
        var Command = ORYX.Core.Command.extend({
          construct: function (changes) {
            this.oldState = changes;
            this.newState = changes.map(function (s) {
              return { shape: s.shape, bounds: s.bounds.clone() };
            });
          },
          execute: function () {
            if (this.executed) {
              this.applyState(this.newState);
            }
            this.executed = true;
          },
          rollback: function () {
            this.applyState(this.oldState);
          },
          applyState: function (state) {
            state.each(function (s) {
              s.shape.bounds.set(s.bounds.upperLeft(), s.bounds.lowerRight());
            });
          },
        });

        this.facade.executeCommands([new Command(changes)]);
      }
    },

    isResized: function (shape, bounds) {
      if (!bounds || !shape) {
        return false;
      }

      var oldB = bounds;
      //var oldXY = oldB.upperLeft();
      //var xy = shape.absoluteXY();
      return Math.round(oldB.width() - shape.bounds.width()) !== 0 || Math.round(oldB.height() - shape.bounds.height()) !== 0;
    },

    adjustLanes: function (pool, lanes, x, y, scale) {
      scale = scale || 0;

      // For every lane, adjust the child nodes with the offset
      lanes.each(
        function (l) {
          l.getChildNodes().each(
            function (child) {
              if (!child.getStencil().id().endsWith('Lane')) {
                var cy = scale ? child.bounds.center().y - child.bounds.center().y / scale : -y;
                child.bounds.moveBy(x || 0, -cy);

                if (scale && child.getStencil().id().endsWith('Subprocess')) {
                  this.moveChildDockers(child, { x: 0, y: -cy });
                }
              }
            }.bind(this)
          );
          this.hashedBounds[pool.id][l.id].moveBy(-(x || 0), !scale ? -y : 0);
          if (scale) {
            l.isScaled = true;
          }
        }.bind(this)
      );
    },

    getAllExcludedLanes: function (parent, lane) {
      var lanes = [];
      parent.getChildNodes().each(
        function (shape) {
          if ((!lane || shape !== lane) && shape.getStencil().id().endsWith('Lane')) {
            lanes.push(shape);
            lanes = lanes.concat(this.getAllExcludedLanes(shape, lane));
          }
        }.bind(this)
      );
      return lanes;
    },

    forceToUpdateLane: function (lane) {
      if (lane.bounds.height() !== lane._svgShapes[0].height) {
        lane.isChanged = true;
        lane.isResized = true;
        lane._update();
      }
    },

    getDepth: function (child, parent) {
      var i = 0;
      while (child && child.parent && child !== parent) {
        child = child.parent;
        ++i;
      }
      return i;
    },

    updateDepth: function (lane, fromDepth, toDepth) {
      var xOffset = (fromDepth - toDepth) * 30;

      lane.getChildNodes().each(function (shape) {
        shape.bounds.moveBy(xOffset, 0);

        [].concat(children[j].getIncomingShapes()).concat(children[j].getOutgoingShapes());
      });
    },

    setDimensions: function (shape, width, height, x, y) {
      var isLane = shape.getStencil().id().endsWith('Lane');
      // Set the bounds
      shape.bounds.set(
        isLane ? 30 : shape.bounds.a.x - (x || 0),
        isLane ? shape.bounds.a.y : shape.bounds.a.y - (y || 0),
        width ? shape.bounds.a.x + width - (isLane ? 30 : x || 0) : shape.bounds.b.x,
        height ? shape.bounds.a.y + height - (isLane ? 0 : y || 0) : shape.bounds.b.y
      );
    },

    setLanePosition: function (shape, y) {
      shape.bounds.moveTo(30, y);
    },

    adjustWidth: function (lanes, width) {
      // Set width to each lane
      (lanes || []).each(
        function (lane) {
          this.setDimensions(lane, width);
          this.adjustWidth(this.getLanes(lane), width - 30);
        }.bind(this)
      );

      return width;
    },

    adjustHeight: function (lanes, changedLane, propagateHeight) {
      var oldHeight = 0;
      if (!changedLane && propagateHeight) {
        var i = -1;
        while (++i < lanes.length) {
          oldHeight += lanes[i].bounds.height();
        }
      }

      var i = -1;
      var height = 0;

      // Iterate trough every lane
      while (++i < lanes.length) {
        if (lanes[i] === changedLane) {
          // Propagate new height down to the children
          this.adjustHeight(this.getLanes(lanes[i]), undefined, lanes[i].bounds.height());

          lanes[i].bounds.set({ x: 30, y: height }, { x: lanes[i].bounds.width() + 30, y: lanes[i].bounds.height() + height });
        } else if (!changedLane && propagateHeight) {
          var tempHeight = (lanes[i].bounds.height() * propagateHeight) / oldHeight;
          // Propagate height
          this.adjustHeight(this.getLanes(lanes[i]), undefined, tempHeight);
          // Set height propotional to the propagated and old height
          this.setDimensions(lanes[i], null, tempHeight);
          this.setLanePosition(lanes[i], height);
        } else {
          // Get height from children
          var tempHeight = this.adjustHeight(this.getLanes(lanes[i]), changedLane, propagateHeight);
          if (!tempHeight) {
            tempHeight = lanes[i].bounds.height();
          }
          this.setDimensions(lanes[i], null, tempHeight);
          this.setLanePosition(lanes[i], height);
        }

        height += lanes[i].bounds.height();
      }

      return height;
    },

    updateHeight: function (root) {
      var lanes = this.getLanes(root);

      if (lanes.length == 0) {
        return root.bounds.height();
      }

      var height = 0;
      var i = -1;
      while (++i < lanes.length) {
        this.setLanePosition(lanes[i], height);
        height += this.updateHeight(lanes[i]);
      }

      this.setDimensions(root, null, height);

      return height;
    },

    getOffset: function (lane, includePool, pool) {
      var offset = { x: 0, y: 0 };

      /*var parent = lane;
                 while(parent) {
                                               
                       
                        var offParent = this.hashedBounds[pool.id][parent.id] ||(includePool === true ? this.hashedPoolPositions[parent.id] : undefined);
                        if (offParent){
                                var ul = parent.bounds.upperLeft();
                                var ulo = offParent.upperLeft();
                                offset.x += ul.x-ulo.x;
                                offset.y += ul.y-ulo.y;
                        }
                       
                        if (parent.getStencil().id().endsWith("Pool")) {
                                break;
                        }
                       
                        parent = parent.parent;
                }       */

      var offset = lane.absoluteXY();

      var hashed = this.hashedBounds[pool.id][lane.id] || (includePool === true ? this.hashedPoolPositions[lane.id] : undefined);
      if (hashed) {
        offset.x -= hashed.upperLeft().x;
        offset.y -= hashed.upperLeft().y;
      } else {
        return { x: 0, y: 0 };
      }
      return offset;
    },

    getNextLane: function (shape) {
      while (shape && !shape.getStencil().id().endsWith('Lane')) {
        if (shape instanceof ORYX.Core.Canvas) {
          return null;
        }
        shape = shape.parent;
      }
      return shape;
    },

    getParentPool: function (shape) {
      while (shape && !shape.getStencil().id().endsWith('Pool')) {
        if (shape instanceof ORYX.Core.Canvas) {
          return null;
        }
        shape = shape.parent;
      }
      return shape;
    },

    updateDockers: function (lanes, pool) {
      var absPool = pool.absoluteBounds(),
        movedShapes = [];
      var oldPool = (this.hashedPoolPositions[pool.id] || absPool).clone();

      var i = -1,
        j = -1,
        k = -1,
        l = -1,
        docker;
      var dockers = {};

      while (++i < lanes.length) {
        if (!this.hashedBounds[pool.id][lanes[i].id]) {
          continue;
        }

        var isScaled = lanes[i].isScaled;
        delete lanes[i].isScaled;
        var children = lanes[i].getChildNodes();
        var absBounds = lanes[i].absoluteBounds();
        var oldBounds = this.hashedBounds[pool.id][lanes[i].id] || absBounds;
        //oldBounds.moveBy((absBounds.upperLeft().x-lanes[i].bounds.upperLeft().x), (absBounds.upperLeft().y-lanes[i].bounds.upperLeft().y));
        var offset = this.getOffset(lanes[i], true, pool);
        var xOffsetDepth = 0;

        var depth = this.getDepth(lanes[i], pool);
        if (this.hashedLaneDepth[lanes[i].id] !== undefined && this.hashedLaneDepth[lanes[i].id] !== depth) {
          xOffsetDepth = (this.hashedLaneDepth[lanes[i].id] - depth) * 30;
          offset.x += xOffsetDepth;
        }

        j = -1;

        while (++j < children.length) {
          if (xOffsetDepth && !children[j].getStencil().id().endsWith('Lane')) {
            movedShapes.push({ xOffset: xOffsetDepth, shape: children[j] });
            children[j].bounds.moveBy(xOffsetDepth, 0);
          }

          if (children[j].getStencil().id().endsWith('Subprocess')) {
            this.moveChildDockers(children[j], offset);
          }

          var edges = []
            .concat(children[j].getIncomingShapes())
            .concat(children[j].getOutgoingShapes())
            // Remove all edges which are included in the selection from the list
            .findAll(function (r) {
              return r instanceof ORYX.Core.Edge;
            });

          k = -1;
          while (++k < edges.length) {
            if (edges[k].getStencil().id().endsWith('MessageFlow')) {
              this.layoutEdges(children[j], [edges[k]], offset);
              continue;
            }

            l = -1;
            while (++l < edges[k].dockers.length) {
              docker = edges[k].dockers[l];

              if (docker.getDockedShape() || docker.isChanged) {
                continue;
              }

              pos = docker.bounds.center();

              // Check if the modified center included the new position
              var isOverLane = oldBounds.isIncluded(pos);
              // Check if the original center is over the pool
              var isOutSidePool = !oldPool.isIncluded(pos);
              var previousIsOverLane = l == 0 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l - 1].bounds.center());
              var nextIsOverLane =
                l == edges[k].dockers.length - 1 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l + 1].bounds.center());
              var off = Object.clone(offset);

              // If the
              if (isScaled && isOverLane && this.isResized(lanes[i], this.hashedBounds[pool.id][lanes[i].id])) {
                var relY = pos.y - absBounds.upperLeft().y + off.y;
                off.y -= relY - relY * (absBounds.height() / oldBounds.height());
              }

              // Check if the previous dockers docked shape is from this lane
              // Otherwise, check if the docker is over the lane OR is outside the lane
              // but the previous/next was over this lane
              if (isOverLane) {
                dockers[docker.id] = { docker: docker, offset: off };
              }
              /*else if (l == 1 && edges[k].dockers.length>2 && edges[k].dockers[l-1].isDocked()){
                                                        var dockedLane = this.getNextLane(edges[k].dockers[l-1].getDockedShape());
                                                        if (dockedLane != lanes[i])
                                                                continue;
                                                        dockers[docker.id] = {docker: docker, offset:offset};
                                                }
                                                // Check if the next dockers docked shape is from this lane
                                                else if (l == edges[k].dockers.length-2 && edges[k].dockers.length>2 && edges[k].dockers[l+1].isDocked()){
                                                        var dockedLane = this.getNextLane(edges[k].dockers[l+1].getDockedShape());
                                                        if (dockedLane != lanes[i])
                                                                continue;
                                                        dockers[docker.id] = {docker: docker, offset:offset};
                                                }
                                                                                               
                                                else if (isOutSidePool) {
                                                        dockers[docker.id] = {docker: docker, offset:this.getOffset(lanes[i], true, pool)};
                                                }*/
            }
          }
        }
      }

      // Move the moved children
      var MoveChildCommand = ORYX.Core.Command.extend({
        construct: function (state) {
          this.state = state;
        },
        execute: function () {
          if (this.executed) {
            this.state.each(function (s) {
              s.shape.bounds.moveBy(s.xOffset, 0);
            });
          }
          this.executed = true;
        },
        rollback: function () {
          this.state.each(function (s) {
            s.shape.bounds.moveBy(-s.xOffset, 0);
          });
        },
      });

      // Set dockers
      this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(dockers), new MoveChildCommand(movedShapes)]);
    },

    moveBy: function (pos, offset) {
      pos.x += offset.x;
      pos.y += offset.y;
      return pos;
    },

    getHashedBounds: function (shape) {
      return this.currentPool && this.hashedBounds[this.currentPool.id][shape.id]
        ? this.hashedBounds[this.currentPool.id][shape.id]
        : shape.absoluteBounds();
    },

    /**
     * Returns a set on all child lanes for the given Shape. If recursive is TRUE, also indirect children will be returned (default is FALSE)
     * The set is sorted with first child the lowest y-coordinate and the last one the highest.
     * @param {ORYX.Core.Shape} shape
     * @param {boolean} recursive
     */
    getLanes: function (shape, recursive) {
      var namespace = this.getNamespace();

      // Get all the child lanes
      var lanes = shape.getChildNodes(recursive || false).findAll(function (node) {
        return node.getStencil().id() === namespace + 'Lane';
      });

      // Sort all lanes by there y coordinate
      lanes = lanes.sort(
        function (a, b) {
          // Get y coordinates for upper left and lower right
          var auy = Math.round(a.bounds.upperLeft().y);
          var buy = Math.round(b.bounds.upperLeft().y);
          var aly = Math.round(a.bounds.lowerRight().y);
          var bly = Math.round(b.bounds.lowerRight().y);

          var ha = this.getHashedBounds(a);
          var hb = this.getHashedBounds(b);

          // Get the old y coordinates
          var oauy = Math.round(ha.upperLeft().y);
          var obuy = Math.round(hb.upperLeft().y);
          var oaly = Math.round(ha.lowerRight().y);
          var obly = Math.round(hb.lowerRight().y);

          // If equal, than use the old one
          if (auy == buy && aly == bly) {
            auy = oauy;
            buy = obuy;
            aly = oaly;
            bly = obly;
          }

          if (Math.round(a.bounds.height() - ha.height()) === 0 && Math.round(b.bounds.height() - hb.height()) === 0) {
            return auy < buy ? -1 : auy > buy ? 1 : 0;
          }

          // Check if upper left and lower right is completely above/below
          var above = auy < buy && aly < bly;
          var below = auy > buy && aly > bly;
          // Check if a is above b including the old values
          var slightlyAboveBottom = auy < buy && aly >= bly && oaly < obly;
          var slightlyAboveTop = auy >= buy && aly < bly && oauy < obuy;
          // Check if a is below b including the old values
          var slightlyBelowBottom = auy > buy && aly <= bly && oaly > obly;
          var slightlyBelowTop = auy <= buy && aly > bly && oauy > obuy;

          // Return -1 if a is above b, 1 if b is above a, or 0 otherwise
          return above || slightlyAboveBottom || slightlyAboveTop ? -1 : below || slightlyBelowBottom || slightlyBelowTop ? 1 : 0;
        }.bind(this)
      );

      // Return lanes
      return lanes;
    },

    getNamespace: function () {
      if (!this.namespace) {
        var stencilsets = this.facade.getStencilSets();
        if (stencilsets.keys()) {
          this.namespace = stencilsets.keys()[0];
        } else {
          return undefined;
        }
      }
      return this.namespace;
    },
  };

  var ResizeLanesCommand = ORYX.Core.Command.extend({
    construct: function (shape, parent, pool, plugin) {
      this.facade = plugin.facade;
      this.plugin = plugin;
      this.shape = shape;
      this.changes;

      this.pool = pool;

      this.parent = parent;

      this.shapeChildren = [];

      /*
       * The Bounds have to be stored
       * separate because they would
       * otherwise also be influenced
       */
      this.shape.getChildShapes().each(
        function (childShape) {
          this.shapeChildren.push({
            shape: childShape,
            bounds: {
              a: {
                x: childShape.bounds.a.x,
                y: childShape.bounds.a.y,
              },
              b: {
                x: childShape.bounds.b.x,
                y: childShape.bounds.b.y,
              },
            },
          });
        }.bind(this)
      );

      this.shapeUpperLeft = this.shape.bounds.upperLeft();

      // If there is no parent,
      // correct the abs position with the parents abs.
      /*if (!this.shape.parent) {
                        var pAbs = parent.absoluteXY();
                        this.shapeUpperLeft.x += pAbs.x;
                        this.shapeUpperLeft.y += pAbs.y;
                }*/
      this.parentHeight = this.parent.bounds.height();
    },

    getLeafLanes: function (lane) {
      var childLanes = this.plugin
        .getLanes(lane)
        .map(
          function (child) {
            return this.getLeafLanes(child);
          }.bind(this)
        )
        .flatten();
      return childLanes.length > 0 ? childLanes : [lane];
    },

    findNewLane: function () {
      var lanes = this.plugin.getLanes(this.parent);

      var leafLanes = this.getLeafLanes(this.parent);
      /*leafLanes = leafLanes.sort(function(a,b){
                        var aupl = a.absoluteXY().y;
                        var bupl = b.absoluteXY().y;
                        return aupl < bupl ? -1 : (aupl > bupl ? 1 : 0)
                })*/
      this.lane =
        leafLanes.find(
          function (l) {
            return l.bounds.upperLeft().y >= this.shapeUpperLeft.y;
          }.bind(this)
        ) || leafLanes.last();
      this.laneUpperLeft = this.lane.bounds.upperLeft();
    },

    execute: function () {
      if (this.changes) {
        this.executeAgain();
        return;
      }

      /*
       * Rescue all ChildShapes of the deleted
       * Shape into the lane that takes its
       * place
       */

      if (!this.lane) {
        this.findNewLane();
      }

      if (this.lane) {
        var laUpL = this.laneUpperLeft;
        var shUpL = this.shapeUpperLeft;

        var depthChange = this.plugin.getDepth(this.lane, this.parent) - 1;

        this.changes = $H({});

        // Selected lane is BELOW the removed lane
        if (laUpL.y >= shUpL.y) {
          this.lane.getChildShapes().each(
            function (childShape) {
              /*
               * Cache the changes for rollback
               */
              if (!this.changes[childShape.getId()]) {
                this.changes[childShape.getId()] = this.computeChanges(childShape, this.lane, this.lane, this.shape.bounds.height());
              }

              childShape.bounds.moveBy(0, this.shape.bounds.height());
            }.bind(this)
          );

          this.plugin.hashChildShapes(this.lane);

          this.shapeChildren.each(
            function (shapeChild) {
              shapeChild.shape.bounds.set(shapeChild.bounds);
              shapeChild.shape.bounds.moveBy(shUpL.x - 30 - depthChange * 30, 0);

              /*
               * Cache the changes for rollback
               */
              if (!this.changes[shapeChild.shape.getId()]) {
                this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0);
              }

              this.lane.add(shapeChild.shape);
            }.bind(this)
          );

          this.lane.bounds.moveBy(0, shUpL.y - laUpL.y);

          // Selected lane is ABOVE the removed lane
        } else if (shUpL.y > laUpL.y) {
          this.shapeChildren.each(
            function (shapeChild) {
              shapeChild.shape.bounds.set(shapeChild.bounds);
              shapeChild.shape.bounds.moveBy(shUpL.x - 30 - depthChange * 30, this.lane.bounds.height());

              /*
               * Cache the changes for rollback
               */
              if (!this.changes[shapeChild.shape.getId()]) {
                this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0);
              }

              this.lane.add(shapeChild.shape);
            }.bind(this)
          );
        }
      }

      /*
       * Adjust the height of the lanes
       */
      // Get the height values
      var oldHeight = this.lane.bounds.height();
      var newHeight = this.lane.length === 1 ? this.parentHeight : this.lane.bounds.height() + this.shape.bounds.height();

      // Set height
      this.setHeight(newHeight, oldHeight, this.parent, this.parentHeight, true);

      // Cache all sibling lanes
      //this.changes[this.shape.getId()] = this.computeChanges(this.shape, this.parent, this.parent, 0);
      this.plugin.getLanes(this.parent).each(
        function (childLane) {
          if (!this.changes[childLane.getId()] && childLane !== this.lane && childLane !== this.shape) {
            this.changes[childLane.getId()] = this.computeChanges(childLane, this.parent, this.parent, 0);
          }
        }.bind(this)
      );

      // Update
      this.update();
    },

    setHeight: function (newHeight, oldHeight, parent, parentHeight, store) {
      // Set heigh of the lane
      this.plugin.setDimensions(this.lane, this.lane.bounds.width(), newHeight);
      this.plugin.hashedBounds[this.pool.id][this.lane.id] = this.lane.absoluteBounds();

      // Adjust child lanes
      this.plugin.adjustHeight(this.plugin.getLanes(parent), this.lane);

      if (store === true) {
        // Store changes
        this.changes[this.shape.getId()] = this.computeChanges(this.shape, parent, parent, 0, oldHeight, newHeight);
      }

      // Set parents height
      this.plugin.setDimensions(parent, parent.bounds.width(), parentHeight);

      if (parent !== this.pool) {
        this.plugin.setDimensions(this.pool, this.pool.bounds.width(), this.pool.bounds.height() + (newHeight - oldHeight));
      }
    },

    update: function () {
      // Hack to prevent the updating of the dockers
      this.plugin.hashedBounds[this.pool.id]['REMOVED'] = true;
      // Update
      //this.facade.getCanvas().update();
    },

    rollback: function () {
      var laUpL = this.laneUpperLeft;
      var shUpL = this.shapeUpperLeft;

      this.changes.each(
        function (pair) {
          var parent = pair.value.oldParent;
          var shape = pair.value.shape;
          var parentHeight = pair.value.parentHeight;
          var oldHeight = pair.value.oldHeight;
          var newHeight = pair.value.newHeight;

          // Move siblings
          if (shape.getStencil().id().endsWith('Lane')) {
            shape.bounds.moveTo(pair.value.oldPosition);
          }

          // If lane
          if (oldHeight) {
            this.setHeight(oldHeight, newHeight, parent, parent.bounds.height() + (oldHeight - newHeight));
            if (laUpL.y >= shUpL.y) {
              this.lane.bounds.moveBy(0, this.shape.bounds.height() - 1);
            }
          } else {
            parent.add(shape);
            shape.bounds.moveTo(pair.value.oldPosition);
          }
        }.bind(this)
      );

      // Update
      //this.update();
    },

    executeAgain: function () {
      this.changes.each(
        function (pair) {
          var parent = pair.value.newParent;
          var shape = pair.value.shape;
          var newHeight = pair.value.newHeight;
          var oldHeight = pair.value.oldHeight;

          // If lane
          if (newHeight) {
            var laUpL = this.laneUpperLeft.y;
            var shUpL = this.shapeUpperLeft.y;

            if (laUpL >= shUpL) {
              this.lane.bounds.moveBy(0, shUpL - laUpL);
            }
            this.setHeight(newHeight, oldHeight, parent, parent.bounds.height() + (newHeight - oldHeight));
          } else {
            parent.add(shape);
            shape.bounds.moveTo(pair.value.newPosition);
          }
        }.bind(this)
      );

      // Update
      this.update();
    },

    computeChanges: function (shape, oldParent, parent, yOffset, oldHeight, newHeight) {
      oldParent = this.changes[shape.getId()] ? this.changes[shape.getId()].oldParent : oldParent;
      var oldPosition = this.changes[shape.getId()] ? this.changes[shape.getId()].oldPosition : shape.bounds.upperLeft();

      var sUl = shape.bounds.upperLeft();

      var pos = { x: sUl.x, y: sUl.y + yOffset };

      var changes = {
        shape: shape,
        parentHeight: oldParent.bounds.height(),
        oldParent: oldParent,
        oldPosition: oldPosition,
        oldHeight: oldHeight,
        newParent: parent,
        newPosition: pos,
        newHeight: newHeight,
      };

      return changes;
    },
  });

  ORYX.Plugins.BPMN2_0 = ORYX.Plugins.AbstractPlugin.extend(ORYX.Plugins.BPMN2_0);
})();
